/**
 * Session suggested-fix + resolution-note preview API (Phase 3).
 *
 * Mirrors backend endpoints under /api/v1/ai-sessions/{id}/...
 * See FLOWPILOT-MIGRATION.md Sections 5.2 + 5.4.
 */
import apiClient from './client'

export type UserDecision = 'one_off' | 'draft_template' | 'build_template' | 'dismissed'

export type FixStatus =
  | 'proposed'
  | 'applied_success'
  | 'applied_failed'
  | 'applied_partial'
  | 'dismissed'

export type FixOutcome =
  | 'applied_success'
  | 'applied_failed'
  | 'applied_partial'
  | 'dismissed'

export interface AIOutcomeProposal {
  fix_id: string
  outcome: 'success' | 'failure' | 'partial'
  reason: string
}

export interface SessionSuggestedFix {
  id: string
  session_id: string
  title: string
  description: string
  confidence_pct: number
  script_template_id: string | null
  ai_drafted_script: string | null
  ai_drafted_parameters: Record<string, unknown> | null
  user_decision: UserDecision | null
  status: FixStatus
  applied_at: string | null
  verified_at: string | null
  partial_notes: string | null
  failure_reason: string | null
  ai_outcome_proposal: AIOutcomeProposal | null
  superseded_at: string | null
  created_at: string
}

export interface DecisionResponse {
  id: string
  user_decision: UserDecision
  rendered_script: string | null
  draft_template_id: string | null
  redirect_path: string | null
}

export interface ResolutionNotePreview {
  markdown: string
  target_ticket_ref: string | null
  state_version: number
  from_cache: boolean
}

export interface ResolutionPostResponse {
  outcome: 'resolved' | 'escalated' | 'resolved_local' | 'escalated_local'
  session_status: string
  external_id: string | null
  posted_at: string | null
  verified_status_id: number | null
  verified_status_name: string | null
  status_transition_skipped_reason: string | null
}

export const sessionSuggestedFixesApi = {
  /**
   * Returns the active suggested fix for a session, or `null` if there isn't one.
   * The endpoint returns 404 in the no-fix case, which is normal — we coerce
   * to null so callers don't have to distinguish "no fix" from "request failed".
   */
  async getActive(sessionId: string): Promise<SessionSuggestedFix | null> {
    try {
      const r = await apiClient.get<SessionSuggestedFix>(
        `/ai-sessions/${sessionId}/suggested-fixes/active`,
      )
      return r.data
    } catch (err) {
      const status = (err as { response?: { status?: number } })?.response?.status
      if (status === 404) return null
      throw err
    }
  },

  async recordDecision(
    sessionId: string,
    fixId: string,
    decision: UserDecision,
    options?: {
      editedScript?: string
      parametersUsed?: Record<string, unknown>
    },
  ): Promise<DecisionResponse> {
    const r = await apiClient.post<DecisionResponse>(
      `/ai-sessions/${sessionId}/suggested-fixes/${fixId}/decision`,
      {
        decision,
        edited_script: options?.editedScript,
        parameters_used: options?.parametersUsed,
      },
    )
    return r.data
  },

  /**
   * Stamp applied_at when the engineer clicks Apply in the ProposalBanner.
   * Does NOT change status (fix remains 'proposed'). Status flips only on
   * a subsequent PATCH /outcome. Idempotent if applied_at is already set.
   * Returns 409 if the fix is no longer in 'proposed' status.
   */
  async applyFix(sessionId: string, fixId: string): Promise<SessionSuggestedFix> {
    const r = await apiClient.post<SessionSuggestedFix>(
      `/ai-sessions/${sessionId}/suggested-fixes/${fixId}/apply`,
    )
    return r.data
  },

  /**
   * Record the outcome of applying a suggested fix. Transition rules:
   * - from `proposed` or `applied_partial`: any outcome is valid (partial is
   *   parked, not terminal — engineer may update notes, abandon via dismiss,
   *   or advance to success/failed).
   * - from a terminal status (`applied_success`, `applied_failed`, `dismissed`):
   *   server returns 409.
   */
  async patchOutcome(
    sessionId: string,
    fixId: string,
    outcome: FixOutcome,
    notes?: string,
  ): Promise<SessionSuggestedFix> {
    const r = await apiClient.patch<SessionSuggestedFix>(
      `/ai-sessions/${sessionId}/suggested-fixes/${fixId}/outcome`,
      { outcome, notes },
    )
    return r.data
  },

  /**
   * Fetch (or get cached) draft markdown for the Resolve note. Backend cache
   * is keyed on state_version, so calling this back-to-back without intervening
   * fact / suggested-fix / script-generation writes returns the same payload
   * cheaply (no Sonnet call).
   */
  async getResolutionNotePreview(sessionId: string): Promise<ResolutionNotePreview> {
    const r = await apiClient.post<ResolutionNotePreview>(
      `/ai-sessions/${sessionId}/resolution-note/preview`,
    )
    return r.data
  },

  /**
   * Parallel to getResolutionNotePreview, for the Escalate flow (five-section
   * handoff markdown). Separate backend cache kind, same state_version invariant.
   */
  async getEscalationPackagePreview(sessionId: string): Promise<ResolutionNotePreview> {
    const r = await apiClient.post<ResolutionNotePreview>(
      `/ai-sessions/${sessionId}/escalation-package/preview`,
    )
    return r.data
  },

  /**
   * Post the engineer-edited resolution markdown to PSA + mark session resolved.
   * Local-only when the session has no linked PSA ticket (outcome = "resolved_local").
   */
  async postResolutionNote(
    sessionId: string,
    markdown: string,
    resolutionSummary?: string,
  ): Promise<ResolutionPostResponse> {
    const r = await apiClient.post<ResolutionPostResponse>(
      `/ai-sessions/${sessionId}/resolution-note/post`,
      { markdown, resolution_summary: resolutionSummary },
    )
    return r.data
  },

  async postEscalationPackage(
    sessionId: string,
    markdown: string,
    escalationReason?: string,
  ): Promise<ResolutionPostResponse> {
    const r = await apiClient.post<ResolutionPostResponse>(
      `/ai-sessions/${sessionId}/escalation-package/post`,
      { markdown, escalation_reason: escalationReason },
    )
    return r.data
  },

  /**
   * Attach an engineer-drafted script to a suggested fix (inline Script
   * Builder Submit path). Does NOT stamp applied_at — the server treats
   * a draft as non-terminal progress. Bumps state_version so the
   * Resolve/Escalate preview regenerates.
   */
  async patchScript(
    sessionId: string,
    fixId: string,
    aiDraftedScript: string,
    aiDraftedParameters?: Record<string, unknown>,
  ): Promise<SessionSuggestedFix> {
    const r = await apiClient.patch<SessionSuggestedFix>(
      `/ai-sessions/${sessionId}/suggested-fixes/${fixId}/script`,
      {
        ai_drafted_script: aiDraftedScript,
        ai_drafted_parameters: aiDraftedParameters,
      },
    )
    return r.data
  },

  /**
   * Explicitly dismiss the AI-proposed outcome banner ("Not yet").
   * Clears ai_outcome_proposal on the server without touching status or
   * state_version. Idempotent: returns 200 even when the field is already null.
   */
  async clearAIProposal(sessionId: string, fixId: string): Promise<SessionSuggestedFix> {
    const r = await apiClient.delete<SessionSuggestedFix>(
      `/ai-sessions/${sessionId}/suggested-fixes/${fixId}/ai-outcome-proposal`,
    )
    return r.data
  },
}

export default sessionSuggestedFixesApi
