import { useMemo, useState } from 'react';
import { Check } from 'lucide-react';
import { toast } from 'sonner';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
import { Button } from '@/components/ui/button';
import type {
  AskUserAnswer,
  AskUserAnswerSet,
  AskUserQuestion,
  ToolCall,
  ToolResult,
} from '@/api/types';

// Batch 9.7. Inline interactive picker. Renders inside MessageList in place of
// the standard ToolCallLine when the assistant emits an ask_user_input tool
// call. While the tool result is null (server pre-stamps a sentinel with
// output=null), shows the form; once the WS tool_result frame arrives with a
// real AnswerSet, flips to read-only review mode.

interface Props {
  toolCall: ToolCall;
  toolResult: ToolResult | null;
  chatId: string;
  apiPrefix?: string;
}

function parseQuestions(raw: unknown): AskUserQuestion[] {
  if (!raw || typeof raw !== 'object' || !('questions' in raw)) return [];
  const arr = (raw as { questions: unknown }).questions;
  if (!Array.isArray(arr)) return [];
  const out: AskUserQuestion[] = [];
  for (const item of arr) {
    if (!item || typeof item !== 'object') continue;
    const q = item as { question?: unknown; type?: unknown; options?: unknown };
    if (typeof q.question !== 'string') continue;
    if (q.type !== 'single_select' && q.type !== 'multi_select') continue;
    if (!Array.isArray(q.options)) continue;
    const opts = q.options.filter((o): o is string => typeof o === 'string');
    if (opts.length < 2) continue;
    out.push({ question: q.question, type: q.type, options: opts });
  }
  return out;
}

function parseAnswerSet(raw: unknown): AskUserAnswerSet | null {
  if (!raw || typeof raw !== 'object' || !('answers' in raw)) return null;
  const arr = (raw as { answers: unknown }).answers;
  if (!Array.isArray(arr)) return null;
  const answers: AskUserAnswer[] = [];
  for (const item of arr) {
    if (!item || typeof item !== 'object') continue;
    const a = item as { question?: unknown; selected_options?: unknown; free_text?: unknown };
    if (typeof a.question !== 'string') continue;
    if (!Array.isArray(a.selected_options)) continue;
    if (a.free_text !== null && typeof a.free_text !== 'string') continue;
    const sel = a.selected_options.filter((s): s is string => typeof s === 'string');
    answers.push({
      question: a.question,
      selected_options: sel,
      free_text: (a.free_text as string | null) ?? null,
    });
  }
  return { answers };
}

export function AskUserInputCard({ toolCall, toolResult, chatId, apiPrefix = '' }: Props) {
  const questions = useMemo(() => parseQuestions(toolCall.args), [toolCall.args]);

  if (questions.length === 0) {
    return (
      <div className="rounded border border-destructive/40 bg-destructive/10 text-xs px-3 py-2 text-destructive">
        ask_user_input: malformed tool args
      </div>
    );
  }

  const answered = toolResult && toolResult.output !== null;
  if (answered) {
    const answerSet = parseAnswerSet(toolResult!.output);
    return <AnsweredView questions={questions} answers={answerSet} />;
  }

  return (
    <PendingView questions={questions} toolCallId={toolCall.id} chatId={chatId} apiPrefix={apiPrefix} />
  );
}

function PendingView({
  questions,
  toolCallId,
  chatId,
  apiPrefix = '',
}: {
  questions: AskUserQuestion[];
  toolCallId: string;
  chatId: string;
  apiPrefix?: string;
}) {
  // Per-question selections + free text. Selections are option arrays so the
  // multi_select case is uniform; single_select just constrains to length 1.
  const [selections, setSelections] = useState<string[][]>(() => questions.map(() => []));
  const [freeTexts, setFreeTexts] = useState<string[]>(() => questions.map(() => ''));
  const [submitting, setSubmitting] = useState(false);

  const singleQuestion = questions.length === 1;
  const anyFreeText = freeTexts.some((t) => t.trim().length > 0);

  // Submit button shows when:
  //   - more than one question (always batched), OR
  //   - one question and the user has typed free text (committing it needs an
  //     explicit Submit so an accidental Tab/click doesn't lose it).
  // For one question with no free text, clicking an option submits inline.
  const showSubmitButton = !singleQuestion || anyFreeText;

  // Every question must have at least one of (option, free text).
  const allComplete = questions.every((_, i) => {
    return selections[i]!.length > 0 || freeTexts[i]!.trim().length > 0;
  });

  function buildAnswers(): AskUserAnswer[] {
    return questions.map((q, i) => {
      const freeText = freeTexts[i]!.trim();
      return {
        question: q.question,
        selected_options: selections[i]!,
        free_text: freeText.length > 0 ? freeText : null,
      };
    });
  }

  async function submit(answers: AskUserAnswer[]) {
    if (submitting) return;
    setSubmitting(true);
    try {
      const url = `${apiPrefix}/api/chats/${chatId}/answer_user_input`;
      const res = await fetch(url, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ tool_call_id: toolCallId, answers }),
      });
      if (!res.ok) {
        const body = await res.json().catch(() => ({})) as { error?: string; detail?: string };
        throw new Error(body.detail ?? body.error ?? `HTTP ${res.status}`);
      }
    } catch (err) {
      toast.error(err instanceof Error ? err.message : 'submit failed');
      setSubmitting(false);
    }
  }

  function pickSingle(qIdx: number, option: string) {
    setSelections((prev) => prev.map((arr, i) => (i === qIdx ? [option] : arr)));
    // Immediate submit for the single-question single-select shortcut. Only
    // fires when no free text exists anywhere — once the user typed, the
    // Submit button takes over so the typed text isn't silently dropped.
    if (singleQuestion && !anyFreeText) {
      const answers: AskUserAnswer[] = [
        {
          question: questions[0]!.question,
          selected_options: [option],
          free_text: null,
        },
      ];
      void submit(answers);
    }
  }

  function toggleMulti(qIdx: number, option: string) {
    setSelections((prev) =>
      prev.map((arr, i) => {
        if (i !== qIdx) return arr;
        return arr.includes(option) ? arr.filter((o) => o !== option) : [...arr, option];
      }),
    );
  }

  function setFreeText(qIdx: number, value: string) {
    setFreeTexts((prev) => prev.map((t, i) => (i === qIdx ? value : t)));
  }

  return (
    <div className="rounded-lg border bg-muted/20 text-sm">
      <div className="px-4 py-3 space-y-4">
        {questions.map((q, i) => (
          <div key={i} className="space-y-2">
            {questions.length > 1 && (
              <div className="text-[10px] uppercase tracking-wide text-muted-foreground/70">
                Question {i + 1}
              </div>
            )}
            <div className="font-medium leading-snug">{q.question}</div>
            {q.type === 'single_select' ? (
              <RadioGroup
                value={selections[i]![0] ?? ''}
                onValueChange={(v) => pickSingle(i, v)}
                disabled={submitting}
                className="gap-1.5"
              >
                {q.options.map((opt, j) => {
                  const id = `q${i}-opt${j}`;
                  return (
                    <label
                      key={j}
                      htmlFor={id}
                      className="flex items-start gap-2 text-sm leading-snug cursor-pointer rounded px-1 py-0.5 hover:bg-muted/40"
                    >
                      <RadioGroupItem id={id} value={opt} className="mt-0.5" />
                      <span>{opt}</span>
                    </label>
                  );
                })}
              </RadioGroup>
            ) : (
              <div className="grid gap-1.5">
                {q.options.map((opt, j) => {
                  const id = `q${i}-opt${j}`;
                  const checked = selections[i]!.includes(opt);
                  return (
                    <label
                      key={j}
                      htmlFor={id}
                      className="flex items-start gap-2 text-sm leading-snug cursor-pointer rounded px-1 py-0.5 hover:bg-muted/40"
                    >
                      <input
                        id={id}
                        type="checkbox"
                        checked={checked}
                        disabled={submitting}
                        onChange={() => toggleMulti(i, opt)}
                        className="mt-1 size-3.5 rounded border-input accent-primary"
                      />
                      <span>{opt}</span>
                    </label>
                  );
                })}
              </div>
            )}
            <div className="pt-1 space-y-1">
              <div className="text-[10px] uppercase tracking-wide text-muted-foreground/70">
                Or type a custom answer
              </div>
              <input
                type="text"
                value={freeTexts[i]}
                disabled={submitting}
                placeholder="Free text…"
                onChange={(e) => setFreeText(i, e.target.value)}
                className="w-full rounded border border-input bg-background px-2 py-1 text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring/40 disabled:opacity-60"
              />
            </div>
          </div>
        ))}
      </div>
      {showSubmitButton && (
        <div className="flex justify-end gap-2 border-t px-4 py-2">
          <Button
            type="button"
            size="sm"
            disabled={!allComplete || submitting}
            onClick={() => void submit(buildAnswers())}
          >
            {submitting ? 'Submitting…' : 'Submit'}
          </Button>
        </div>
      )}
    </div>
  );
}

function AnsweredView({
  questions,
  answers,
}: {
  questions: AskUserQuestion[];
  answers: AskUserAnswerSet | null;
}) {
  if (!answers) {
    return (
      <div className="rounded-lg border bg-muted/20 text-xs px-4 py-3 text-muted-foreground">
        ask_user_input: answers unavailable
      </div>
    );
  }

  return (
    <div className="rounded-lg border bg-muted/10 text-sm">
      <div className="px-4 py-3 space-y-3">
        {questions.map((q, i) => {
          const a = answers.answers[i];
          if (!a) return null;
          return (
            <div key={i} className="space-y-1.5">
              {questions.length > 1 && (
                <div className="text-[10px] uppercase tracking-wide text-muted-foreground/70">
                  Question {i + 1}
                </div>
              )}
              <div className="font-medium leading-snug">{q.question}</div>
              <div className="space-y-0.5">
                {q.options.map((opt, j) => {
                  const selected = a.selected_options.includes(opt);
                  return (
                    <div
                      key={j}
                      className={
                        selected
                          ? 'flex items-start gap-2 text-sm leading-snug text-foreground'
                          : 'flex items-start gap-2 text-sm leading-snug text-muted-foreground/60 line-through'
                      }
                    >
                      <span className="mt-0.5 size-3.5 shrink-0 inline-flex items-center justify-center">
                        {selected && <Check className="size-3 text-primary" />}
                      </span>
                      <span>{opt}</span>
                    </div>
                  );
                })}
              </div>
              {a.free_text && (
                <div className="rounded bg-background border px-2 py-1 text-xs font-mono whitespace-pre-wrap">
                  {a.free_text}
                </div>
              )}
            </div>
          );
        })}
      </div>
    </div>
  );
}
