'use client';

/**
 * ScheduleQuickCreate — V3 D entry point (#266).
 *
 * Two stages:
 *   1. Natural-language textarea → POST /api/schedules/extract
 *   2. ScheduleConfirmCard renders the parsed spec, lets user adjust
 *      fields inline, then POSTs to /api/prompts + /api/schedules.
 *
 * Always half-automatic — Forge never auto-creates the schedule. The
 * user must Confirm. Low confidence (< 0.7) or non-empty `missing[]`
 * shows a banner asking them to verify before saving.
 *
 * Chat-thread integration (extractor invoked mid-chat with a Confirm Card
 * inline) is a follow-up — same Card component will be reused there.
 */

import { useEffect, useState } from 'react';

interface Extracted {
  name: string;
  prompt_name: string;
  prompt: string;
  agent: string | null;
  skills: string[];
  project: string | null;
  trigger: {
    kind: 'period' | 'cron' | 'once' | 'manual';
    interval_minutes: number | null;
    cron: string | null;
    at_iso: string | null;
  };
  action: {
    kind: 'none' | 'chat' | 'email' | 'telegram';
    config: Record<string, unknown>;
  };
  confidence: number;
  missing: string[];
}

interface AgentInfo { id: string; label?: string; backendType?: string }
interface ProjectInfo { name: string; path: string }

interface Props {
  onClose: () => void;
  onCreated: () => void;
}

export default function ScheduleQuickCreate({ onClose, onCreated }: Props) {
  const [text, setText] = useState('');
  const [extracting, setExtracting] = useState(false);
  const [extracted, setExtracted] = useState<Extracted | null>(null);
  const [err, setErr] = useState('');

  async function extract() {
    setErr('');
    setExtracting(true);
    try {
      const r = await fetch('/api/schedules/extract', {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({ text: text.trim() }),
      });
      const j = await r.json();
      if (!r.ok || !j.ok) {
        setErr(j.error || `HTTP ${r.status}`);
        return;
      }
      setExtracted(j.extracted as Extracted);
    } catch (e) {
      setErr(e instanceof Error ? e.message : String(e));
    } finally {
      setExtracting(false);
    }
  }

  return (
    <div className="fixed inset-0 z-50 flex items-start justify-center bg-black/50 pt-[10vh]">
      <div className="bg-[var(--bg-primary)] border border-[var(--border)] rounded shadow-2xl w-[680px] max-w-[95vw] max-h-[85vh] flex flex-col">
        <div className="px-4 py-3 border-b border-[var(--border)] flex items-center">
          <h2 className="text-[13px] font-semibold flex-1">✨ Quick create schedule</h2>
          <button onClick={onClose} className="text-[14px] text-[var(--text-secondary)] hover:text-[var(--text-primary)] w-6 h-6">✕</button>
        </div>

        <div className="flex-1 overflow-auto p-4">
          {!extracted && (
            <>
              <div className="text-[11px] text-[var(--text-secondary)] mb-2">
                Describe what you want Forge to run on a schedule. Be specific about
                <em> when</em>, <em>what</em>, and <em>where to send the result</em>.
                Forge will parse it into a draft you can review and tweak before saving.
              </div>
              <textarea
                value={text}
                onChange={(e) => setText(e.target.value)}
                rows={6}
                placeholder="e.g. 每天上午 9 点查一下我今天分到的 mantis bug,发我 Telegram"
                className="w-full text-[12px] font-mono px-3 py-2 border border-[var(--border)] rounded bg-[var(--bg-secondary)] resize-y"
              />
              <div className="text-[10px] text-[var(--text-secondary)] mt-1">
                Your wording stays verbatim — Forge does NOT rewrite it.
              </div>
            </>
          )}

          {extracted && (
            <ScheduleConfirmCard
              initial={extracted}
              originalText={text}
              onCancel={() => setExtracted(null)}
              onCreated={onCreated}
            />
          )}

          {err && <div className="text-[11px] text-[var(--red)] bg-[var(--red)]/10 rounded p-2 mt-3">{err}</div>}
        </div>

        {!extracted && (
          <div className="px-4 py-3 border-t border-[var(--border)] flex items-center justify-between">
            <button onClick={onClose} className="text-[11px] px-3 py-1 text-[var(--text-secondary)] hover:text-[var(--text-primary)]">Cancel</button>
            <button
              onClick={() => void extract()}
              disabled={!text.trim() || extracting}
              className="text-[11px] px-3 py-1 bg-[var(--accent)] text-[var(--bg-primary)] rounded font-semibold disabled:opacity-50"
            >{extracting ? 'Parsing…' : 'Parse →'}</button>
          </div>
        )}
      </div>
    </div>
  );
}

function ScheduleConfirmCard({
  initial, originalText, onCancel, onCreated,
}: {
  initial: Extracted;
  originalText: string;
  onCancel: () => void;
  onCreated: () => void;
}) {
  const [name, setName] = useState(initial.name || initial.prompt_name || 'untitled');
  const [promptText, setPromptText] = useState(initial.prompt);
  const [agent, setAgent] = useState(initial.agent || '');
  const [skills, setSkills] = useState<string[]>(initial.skills);
  const [skillsRaw, setSkillsRaw] = useState(initial.skills.join(', '));
  const [project, setProject] = useState(initial.project || '');
  const [triggerKind, setTriggerKind] = useState(initial.trigger.kind);
  const [interval, setInterval] = useState(initial.trigger.interval_minutes ?? 30);
  const [cron, setCron] = useState(initial.trigger.cron ?? '');
  const [atIso, setAtIso] = useState(initial.trigger.at_iso ?? '');
  const [actionKind, setActionKind] = useState(initial.action.kind);
  const [actionConfigJson, setActionConfigJson] = useState(
    JSON.stringify(initial.action.config ?? {}, null, 2)
  );

  const [agents, setAgents] = useState<AgentInfo[]>([]);
  const [projects, setProjects] = useState<ProjectInfo[]>([]);
  const [submitting, setSubmitting] = useState(false);
  const [err, setErr] = useState('');

  const lowConfidence = initial.confidence < 0.7 || initial.missing.length > 0;

  useEffect(() => {
    (async () => {
      try {
        const [skR, agR] = await Promise.all([
          fetch('/api/skills').then((r) => r.json()),
          fetch('/api/agents?include=all').then((r) => r.json()).catch(() => ({ agents: [] })),
        ]);
        if (Array.isArray(skR?.projects)) setProjects(skR.projects);
        if (Array.isArray(agR?.agents)) setAgents(agR.agents);
      } catch {/* ignore — fields fall back to free text */}
    })();
  }, []);

  async function submit() {
    setErr('');
    setSubmitting(true);
    try {
      const cleanedSkills = skillsRaw
        .split(/[,\s]+/)
        .map((s) => s.trim())
        .filter(Boolean);
      // 1. Create prompts/<name>.yaml. Auto-suffix on 409 (same logic as
      //    ScheduleCreateModal — keeps quick-create idempotent across retries).
      const base = slugifyName(name) || initial.prompt_name || `prompt-${Date.now().toString(36).slice(-6)}`;
      let promptNameToUse: string | null = null;
      let candidate = base;
      for (let i = 2; i <= 50; i++) {
        const r = await fetch('/api/prompts', {
          method: 'POST',
          headers: { 'content-type': 'application/json' },
          body: JSON.stringify({
            name: candidate,
            prompt: promptText,
            executor: { agent: agent || null, skills: cleanedSkills },
          }),
        });
        if (r.ok) { promptNameToUse = candidate; break; }
        if (r.status === 409) { candidate = `${base}-${i}`; continue; }
        const j = await r.json().catch(() => ({}));
        setErr(j.error || `prompt save failed (${r.status})`);
        return;
      }
      if (!promptNameToUse) {
        setErr(`couldn't find a free prompt name starting from "${base}"`);
        return;
      }

      // 2. Create the schedule
      let actionConfig: Record<string, unknown>;
      try {
        actionConfig = actionConfigJson.trim() ? JSON.parse(actionConfigJson) : {};
      } catch (e) {
        setErr(`action_config is not valid JSON: ${(e as Error).message}`);
        return;
      }
      const body: Record<string, unknown> = {
        name,
        body_kind: 'prompt',
        body_ref: promptNameToUse,
        input: { project },
        enabled: true,
        schedule_kind: triggerKind,
        schedule_interval_minutes: triggerKind === 'period' ? interval : null,
        schedule_cron: triggerKind === 'cron' ? cron.trim() : null,
        schedule_at: triggerKind === 'once' && atIso ? new Date(atIso).toISOString() : null,
        action_kind: actionKind,
        action_config: actionConfig,
      };
      const r = await fetch('/api/schedules', {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify(body),
      });
      if (!r.ok) {
        const j = await r.json().catch(() => ({}));
        setErr(j.error || `schedule create failed (${r.status})`);
        return;
      }
      onCreated();
    } catch (e) {
      setErr(e instanceof Error ? e.message : String(e));
    } finally {
      setSubmitting(false);
    }
  }

  return (
    <div className="space-y-3">
      <div className="text-[11px] text-[var(--text-secondary)]">
        Forge parsed your request below. Review, tweak anything, then Create.
      </div>

      {lowConfidence && (
        <div className="text-[11px] text-[var(--yellow)] bg-[var(--yellow)]/10 rounded p-2 border border-[var(--yellow)]/30">
          ⚠ confidence {initial.confidence.toFixed(2)}
          {initial.missing.length > 0 && <> · missing: <code className="font-mono">{initial.missing.join(', ')}</code></>}
          <div className="text-[10px] text-[var(--text-secondary)] mt-0.5">Verify the fields before saving.</div>
        </div>
      )}

      <Field label="Schedule name">
        <input value={name} onChange={(e) => setName(e.target.value)} className={inputCls} />
      </Field>

      <Field label="Prompt (verbatim)" hint="The agent will receive this exact text. Tweak if Forge mis-quoted.">
        <textarea
          value={promptText}
          onChange={(e) => setPromptText(e.target.value)}
          rows={5}
          className={inputCls + ' font-mono'}
        />
      </Field>

      <div className="grid grid-cols-2 gap-3">
        <Field label="Project" hint="Leave on scratch for prompts that just call connectors.">
          <select value={project} onChange={(e) => setProject(e.target.value)} className={inputCls}>
            <option value="">— scratch (default) —</option>
            {projects.filter((p) => p.name !== 'scratch').map((p) => <option key={p.name} value={p.name}>{p.name}</option>)}
            {project && project !== 'scratch' && !projects.some((p) => p.name === project) && (
              <option value={project}>{project} (not in projects list)</option>
            )}
          </select>
        </Field>
        <Field label="Agent">
          <select value={agent} onChange={(e) => setAgent(e.target.value)} className={inputCls}>
            <option value="">— system default —</option>
            {agents.map((a) => <option key={a.id} value={a.id}>{a.label || a.id}</option>)}
          </select>
        </Field>
      </div>

      <Field label="Skills" hint="Comma-separated. ai-orchestration is the default V3 skill (lets the agent call connectors).">
        <input value={skillsRaw} onChange={(e) => setSkillsRaw(e.target.value)} className={inputCls + ' font-mono'} />
      </Field>

      <Field label="Trigger">
        <div className="flex gap-2 items-baseline">
          <select
            value={triggerKind}
            onChange={(e) => setTriggerKind(e.target.value as Extracted['trigger']['kind'])}
            className={inputCls + ' w-32'}
          >
            <option value="period">period</option>
            <option value="cron">cron</option>
            <option value="once">once</option>
            <option value="manual">manual</option>
          </select>
          {triggerKind === 'period' && (
            <>
              <input
                type="number" min={1}
                value={interval}
                onChange={(e) => setInterval(Number(e.target.value) || 1)}
                className={inputCls + ' w-24'}
              />
              <span className="text-[10px] text-[var(--text-secondary)]">minutes</span>
            </>
          )}
          {triggerKind === 'cron' && (
            <input value={cron} onChange={(e) => setCron(e.target.value)} placeholder="e.g. 0 9 * * *"
              className={inputCls + ' font-mono flex-1'} />
          )}
          {triggerKind === 'once' && (
            <input
              type="datetime-local"
              value={atIso ? new Date(atIso).toISOString().slice(0, 16) : ''}
              onChange={(e) => setAtIso(e.target.value ? new Date(e.target.value).toISOString() : '')}
              className={inputCls + ' flex-1'}
            />
          )}
        </div>
      </Field>

      <Field label="Action">
        <div className="flex gap-2 items-start">
          <select
            value={actionKind}
            onChange={(e) => setActionKind(e.target.value as Extracted['action']['kind'])}
            className={inputCls + ' w-32'}
          >
            <option value="none">none</option>
            <option value="chat">chat</option>
            <option value="email">email</option>
            <option value="telegram">telegram</option>
          </select>
          {actionKind !== 'none' && (
            <textarea
              value={actionConfigJson}
              onChange={(e) => setActionConfigJson(e.target.value)}
              rows={3}
              placeholder='{ "chat_id": "..." }'
              className={inputCls + ' font-mono flex-1'}
            />
          )}
        </div>
        {actionKind !== 'none' && (
          <div className="text-[10px] text-[var(--text-secondary)] mt-1">
            JSON object; see Schedules help doc for the per-action shape. Chat needs <code className="font-mono">session_id</code>, email needs <code className="font-mono">to</code>, telegram needs <code className="font-mono">chat_id</code> (or default subscriber).
          </div>
        )}
      </Field>

      {err && <div className="text-[11px] text-[var(--red)] bg-[var(--red)]/10 rounded p-2">{err}</div>}

      <details className="text-[10px] text-[var(--text-secondary)]">
        <summary className="cursor-pointer">Original text</summary>
        <pre className="mt-1 whitespace-pre-wrap break-words font-mono">{originalText}</pre>
      </details>

      <div className="flex justify-between pt-2 border-t border-[var(--border)]">
        <button onClick={onCancel} className="text-[11px] px-3 py-1 text-[var(--text-secondary)] hover:text-[var(--text-primary)]">← Re-parse</button>
        <button
          onClick={() => void submit()}
          disabled={submitting || !promptText.trim() || !name.trim() || (triggerKind === 'cron' && !cron.trim()) || (triggerKind === 'once' && !atIso)}
          className="text-[11px] px-3 py-1 bg-[var(--accent)] text-[var(--bg-primary)] rounded font-semibold disabled:opacity-50"
        >{submitting ? 'Creating…' : 'Confirm create'}</button>
      </div>
    </div>
  );
}

const inputCls = 'text-[11px] px-2 py-1 border border-[var(--border)] rounded bg-[var(--bg-secondary)] w-full';

function Field({ label, hint, children }: { label: string; hint?: string; children: React.ReactNode }) {
  return (
    <div>
      <div className="text-[11px] mb-0.5">{label}</div>
      {children}
      {hint && <div className="text-[10px] text-[var(--text-secondary)] mt-0.5">{hint}</div>}
    </div>
  );
}

// Mirror the slug rule in ScheduleCreateModal so quick-create + manual create
// produce comparable prompt filenames.
function slugifyName(s: string): string {
  return s.toLowerCase()
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-+|-+$/g, '')
    .slice(0, 50);
}
