import type {
  AssistantRequirement,
  RequirementCoverageReport,
  UnifiedRetrievalResult
} from "../types/assistant";
import type { RouteHintSummary } from "../types/normalizer";
import type {
  InvestigationLastAnswerMode,
  InvestigationNarrowingStatus,
  InvestigationState
} from "../types/stage1Contracts";
import {
  INVESTIGATION_MAX_EVIDENCE_REFS,
  INVESTIGATION_MAX_PRIMARY_ACCOUNTS,
  INVESTIGATION_MAX_REQUIREMENT_LINKS,
  INVESTIGATION_MAX_UNCERTAINTIES,
  INVESTIGATION_STATE_SCHEMA_VERSION
} from "../types/stage1Contracts";

interface UpdateInvestigationStateInput {
  previous: InvestigationState;
  timestamp: string;
  questionId: string;
  userMessage: string;
  routeSummary: RouteHintSummary | null;
  requirements: AssistantRequirement[];
  coverageReport: RequirementCoverageReport;
  retrievalResults: UnifiedRetrievalResult[];
  replyType: InvestigationLastAnswerMode;
}

function uniqueStrings(values: string[]): string[] {
  return Array.from(new Set(values.map((item) => item.trim()).filter(Boolean)));
}

function capStrings(values: string[], max: number): string[] {
  return uniqueStrings(values).slice(0, max);
}

function detectAccounts(text: string): string[] {
  return capStrings(text.match(/\b\d{2}(?:\.\d{2})?\b/g) ?? [], INVESTIGATION_MAX_PRIMARY_ACCOUNTS);
}

function detectPeriod(text: string): string | null {
  const monthly = text.match(/\b(20\d{2})[-/.](0[1-9]|1[0-2])\b/);
  if (monthly) return `${monthly[1]}-${monthly[2]}`;
  const yearly = text.match(/\b(20\d{2})\b/);
  if (yearly) return yearly[1];
  return null;
}

function deriveDomain(routeSummary: RouteHintSummary | null): string | null {
  if (!routeSummary) return null;
  if (routeSummary.mode === "legacy_v1") {
    return routeSummary.route_hint;
  }
  const routes = routeSummary.decisions.map((item) => item.route).filter((route) => route !== "no_route");
  const uniqueRoutes = uniqueStrings(routes);
  if (uniqueRoutes.length === 0) {
    return "no_route";
  }
  return uniqueRoutes.join(",");
}

function deriveNarrowingStatus(
  routeSummary: RouteHintSummary | null,
  coverageReport: RequirementCoverageReport
): InvestigationNarrowingStatus {
  if (!routeSummary) {
    return "unknown";
  }

  if (routeSummary.mode === "legacy_v1") {
    return "not_needed";
  }

  if (routeSummary.fallback.type === "clarification" || coverageReport.clarification_needed_for.length > 0) {
    return "needs_clarification";
  }

  const hasNoRoute = routeSummary.decisions.some((item) => item.route === "no_route");
  if (hasNoRoute) {
    return "broad_guarded";
  }

  return routeSummary.decisions.length > 1 ? "applied" : "not_needed";
}

function deriveQueryModeHint(routeSummary: RouteHintSummary | null): InvestigationState["query_mode_hint"] {
  if (!routeSummary) {
    return "investigation_candidate";
  }
  if (routeSummary.mode === "legacy_v1") {
    return "direct_answer";
  }
  return routeSummary.fallback.type === "none" ? "direct_answer" : "investigation_candidate";
}

function collectEvidenceRefs(retrievalResults: UnifiedRetrievalResult[]): string[] {
  const refs = retrievalResults.flatMap((result) => result.evidence.map((item) => item.evidence_id));
  return capStrings(refs, INVESTIGATION_MAX_EVIDENCE_REFS);
}

function collectOpenUncertainties(
  coverageReport: RequirementCoverageReport,
  retrievalResults: UnifiedRetrievalResult[]
): string[] {
  const requirementNotes = [
    ...coverageReport.requirements_uncovered.map((item) => `uncovered:${item}`),
    ...coverageReport.requirements_partially_covered.map((item) => `partial:${item}`),
    ...coverageReport.clarification_needed_for.map((item) => `clarify:${item}`),
    ...coverageReport.out_of_scope_requirements.map((item) => `out_of_scope:${item}`)
  ];
  const limitationNotes = retrievalResults.flatMap((result) => result.limitations).slice(0, 6);
  return capStrings([...requirementNotes, ...limitationNotes], INVESTIGATION_MAX_UNCERTAINTIES);
}

export function cloneInvestigationState(state: InvestigationState | null): InvestigationState | null {
  if (!state) return null;
  return {
    ...state,
    focus: {
      ...state.focus,
      primary_accounts: [...state.focus.primary_accounts]
    },
    evidence_refs: [...state.evidence_refs],
    open_uncertainties: [...state.open_uncertainties],
    followup_context: state.followup_context
      ? {
          ...state.followup_context,
          referenced_requirement_ids: [...state.followup_context.referenced_requirement_ids]
        }
      : null
  };
}

export function createEmptyInvestigationState(sessionId: string, timestamp = new Date().toISOString()): InvestigationState {
  return {
    schema_version: INVESTIGATION_STATE_SCHEMA_VERSION,
    session_id: sessionId,
    status: "idle",
    turn_index: 0,
    updated_at: timestamp,
    question_id: null,
    focus: {
      domain: null,
      period: null,
      primary_accounts: [],
      active_query_subject: null
    },
    narrowing_status: "unknown",
    evidence_refs: [],
    open_uncertainties: [],
    last_answer_mode: null,
    followup_context: null,
    query_mode_hint: "direct_answer"
  };
}

export function updateInvestigationState(input: UpdateInvestigationStateInput): InvestigationState {
  const previous = input.previous;
  const focusFromMessage = capStrings(detectAccounts(input.userMessage), INVESTIGATION_MAX_PRIMARY_ACCOUNTS);
  const requirementIds = capStrings(
    input.requirements.map((item) => item.requirement_id),
    INVESTIGATION_MAX_REQUIREMENT_LINKS
  );
  const mainRequirement = input.requirements[0]?.requirement_text ?? input.userMessage;

  return {
    schema_version: INVESTIGATION_STATE_SCHEMA_VERSION,
    session_id: previous.session_id,
    status: "active",
    turn_index: previous.turn_index + 1,
    updated_at: input.timestamp,
    question_id: input.questionId,
    focus: {
      domain: deriveDomain(input.routeSummary) ?? previous.focus.domain,
      period: detectPeriod(input.userMessage) ?? previous.focus.period,
      primary_accounts: capStrings(
        [...focusFromMessage, ...previous.focus.primary_accounts],
        INVESTIGATION_MAX_PRIMARY_ACCOUNTS
      ),
      active_query_subject: mainRequirement.slice(0, 180)
    },
    narrowing_status: deriveNarrowingStatus(input.routeSummary, input.coverageReport),
    evidence_refs: capStrings(
      [...collectEvidenceRefs(input.retrievalResults), ...previous.evidence_refs],
      INVESTIGATION_MAX_EVIDENCE_REFS
    ),
    open_uncertainties: collectOpenUncertainties(input.coverageReport, input.retrievalResults),
    last_answer_mode: input.replyType,
    followup_context: {
      previous_question_id: previous.question_id,
      last_user_message: input.userMessage.slice(0, 240),
      referenced_requirement_ids: requirementIds
    },
    query_mode_hint: deriveQueryModeHint(input.routeSummary)
  };
}
