import { createHash } from 'node:crypto'
import { resolveAssistantAction } from '../assistant-action-resolver.mjs'

const OPS_PRODUCT_API_ADAPTER_ID = 'adapter.ops.product_api'
const OPS_ACTIONS = new Set([
  'ops.card.list_recent',
  'ops.card.create',
  'ops.card.add_comment',
])
const OPS_CARD_PRIORITIES = new Set(['none', 'low', 'medium', 'high', 'urgent'])

function cleanString(value) {
  return String(value || '').trim()
}

function cleanText(value, max = 4000) {
  const text = cleanString(value)
  if (!text) return ''
  return text.length > max ? text.slice(0, max).trim() : text
}

function cleanLimit(value) {
  const number = Number(value)
  if (!Number.isFinite(number)) return 5
  return Math.min(20, Math.max(1, Math.trunc(number)))
}

function firstString(...values) {
  for (const value of values) {
    const text = cleanString(value)
    if (text) return text
  }
  return ''
}

function normalizeProjectQuery(input = {}) {
  return {
    workspaceSlug: firstString(
      input.workspaceSlug,
      input.workspace_slug,
      input.opsWorkspaceSlug,
      input.ops_workspace_slug,
      input.context?.workspaceSlug,
      input.context?.opsWorkspaceSlug,
    ),
    projectId: firstString(
      input.projectId,
      input.project_id,
      input.opsProjectId,
      input.ops_project_id,
      input.context?.projectId,
      input.context?.opsProjectId,
    ),
  }
}

function cleanPriority(value) {
  const text = cleanString(value).toLowerCase()
  return OPS_CARD_PRIORITIES.has(text) ? text : ''
}

function cleanIssueId(value) {
  return cleanString(value).replace(/^#/, '')
}

function idempotencyKeyFor(actionId, input, fallbackParts = {}) {
  const explicit = firstString(
    input.idempotencyKey,
    input.idempotency_key,
    input.context?.idempotencyKey,
    input.context?.idempotency_key,
  )
  if (explicit) return explicit

  const digest = createHash('sha256')
    .update(JSON.stringify({
      actionId,
      actorUserId: input.actorUserId || null,
      actorEmail: input.actorEmail || null,
      ...fallbackParts,
    }))
    .digest('hex')
    .slice(0, 24)
  return `ai-workspace:${actionId}:${digest}`
}

function buildPlanForAction(actionId, input = {}, options = {}) {
  switch (actionId) {
    case 'ops.card.list_recent': {
      const normalized = normalizeProjectQuery(input)
      const workspaceSlug = normalized.workspaceSlug || cleanString(options.defaultOpsWorkspaceSlug)
      const projectId = normalized.projectId || cleanString(options.defaultOpsProjectId)
      const limit = cleanLimit(input.limit)

      if (!projectId) {
        return {
          ok: false,
          decision: 'ops_project_context_missing',
          reason: 'OPS project id is required for reading cards.',
        }
      }

      return {
        ok: true,
        request: {
          method: 'GET',
          path: '/api/v1/tools/issues',
          query: {
            project_id: projectId,
            ...(workspaceSlug ? { workspace_slug: workspaceSlug } : {}),
            ...(cleanString(input.query || input.search) ? { query: cleanString(input.query || input.search) } : {}),
          },
          summary: 'Read recent OPS Product cards through local Ops Gateway REST tool route.',
        },
        opsContext: {
          workspaceSlug,
          projectId,
          limit,
        },
      }
    }
    case 'ops.card.create': {
      const normalized = normalizeProjectQuery(input)
      const workspaceSlug = normalized.workspaceSlug || cleanString(options.defaultOpsWorkspaceSlug)
      const projectId = normalized.projectId || cleanString(options.defaultOpsProjectId)
      const title = cleanText(
        firstString(input.title, input.name, input.cardTitle, input.taskTitle, input.summary),
        260,
      )
      const description = cleanText(
        firstString(input.description, input.body, input.details, input.text, input.message),
        12000,
      )
      const priority = cleanPriority(input.priority)

      if (!projectId) {
        return {
          ok: false,
          decision: 'ops_project_context_missing',
          reason: 'OPS project id is required for creating a card.',
        }
      }
      if (!title) {
        return {
          ok: false,
          decision: 'ops_card_title_missing',
          reason: 'OPS card title is required.',
        }
      }

      const body = {
        project_id: projectId,
        ...(workspaceSlug ? { workspace_slug: workspaceSlug } : {}),
        title,
        ...(description ? { description } : {}),
        ...(priority ? { priority } : {}),
      }

      return {
        ok: true,
        request: {
          method: 'POST',
          path: '/api/v1/tools/issues',
          headers: {
            'Idempotency-Key': idempotencyKeyFor(actionId, input, body),
          },
          body,
          summary: 'Create OPS Product card through local Ops Gateway REST tool route.',
        },
        opsContext: {
          workspaceSlug,
          projectId,
        },
      }
    }
    case 'ops.card.add_comment': {
      const normalized = normalizeProjectQuery(input)
      const workspaceSlug = normalized.workspaceSlug || cleanString(options.defaultOpsWorkspaceSlug)
      const projectId = normalized.projectId || cleanString(options.defaultOpsProjectId)
      const issueId = cleanIssueId(firstString(
        input.issueId,
        input.issue_id,
        input.cardId,
        input.card_id,
        input.targetCardId,
        input.target_card_id,
        input.context?.issueId,
        input.context?.issue_id,
        input.context?.cardId,
        input.context?.card_id,
      ))
      const bodyText = cleanText(
        firstString(input.body, input.comment, input.text, input.message),
        12000,
      )

      if (!projectId) {
        return {
          ok: false,
          decision: 'ops_project_context_missing',
          reason: 'OPS project id is required for commenting on a card.',
        }
      }
      if (!issueId) {
        return {
          ok: false,
          decision: 'ops_issue_id_missing',
          reason: 'OPS issue id is required for commenting on a card.',
        }
      }
      if (!bodyText) {
        return {
          ok: false,
          decision: 'ops_comment_body_missing',
          reason: 'OPS comment body is required.',
        }
      }

      const body = {
        project_id: projectId,
        ...(workspaceSlug ? { workspace_slug: workspaceSlug } : {}),
        body: bodyText,
      }

      return {
        ok: true,
        request: {
          method: 'POST',
          path: `/api/v1/tools/issues/${encodeURIComponent(issueId)}/comments`,
          headers: {
            'Idempotency-Key': idempotencyKeyFor(actionId, input, { issueId, ...body }),
          },
          body,
          summary: 'Append comment to OPS Product card through local Ops Gateway REST tool route.',
        },
        opsContext: {
          workspaceSlug,
          projectId,
          issueId,
        },
      }
    }
    default:
      return {
        ok: false,
        decision: 'unsupported_ops_action',
        reason: `Unsupported OPS action: ${actionId}`,
      }
  }
}

export async function buildOpsProductPlan(input = {}, options = {}) {
  const decision = options.decision || await resolveAssistantAction(input, options)
  const actionId = decision.action?.id

  if (!actionId || !OPS_ACTIONS.has(actionId)) {
    return {
      ok: false,
      decision: 'unsupported_ops_action',
      reason: 'Input did not resolve to a supported OPS Product action.',
      actionDecision: decision,
    }
  }

  if (decision.decision !== 'allowed') {
    return {
      ok: false,
      decision: decision.decision,
      reason: decision.reason,
      actionDecision: decision,
    }
  }

  const actionPlan = buildPlanForAction(actionId, input, options)
  if (!actionPlan.ok) {
    return {
      ...actionPlan,
      actionDecision: decision,
    }
  }

  return {
    ok: true,
    decision: 'planned',
    adapterId: OPS_PRODUCT_API_ADAPTER_ID,
    adapterStatus: decision.action.adapterStatus,
    executionReady: decision.execution?.executionReady === true,
    dryRunOnly: decision.action.adapterStatus !== 'implemented',
    actionDecision: decision,
    gatewayActor: {
      userId: input.actorUserId || null,
      email: input.actorEmail || null,
      subject: input.actorSubject || null,
    },
    request: actionPlan.request,
    opsContext: actionPlan.opsContext,
    safety: {
      noDeleteRouteMapped: true,
      sourceGuardedRoutesOnly: true,
      confirmationAlreadyChecked: true,
    },
  }
}
