import { useState } from 'react';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { listBrowserSessionProfiles } from '../../api';
import { useBackdropClose } from '../../lib/useBackdropClose';

export interface ParamHint {
  name: string;
  valueToReplace: string;
  type: 'string' | 'number' | 'boolean';
}

interface SaveAsScriptDialogProps {
  /** The name of the recording (without .json extension). */
  recordingName: string;
  onClose: () => void;
  /** Called with the new script filename (e.g. "my-script.js") after a successful compile. */
  onSuccess: (scriptName: string) => void;
}

const SCRIPT_NAME_RE = /^[A-Za-z0-9_\-.]+$/;

async function apiCompileScript(body: {
  recordingName: string;
  scriptName: string;
  description: string;
  sessionProfileId?: number;
  paramHints?: ParamHint[];
  overwrite?: boolean;
}): Promise<{ ok: boolean; scriptName: string }> {
  const { overwrite, ...rest } = body;
  const qs = overwrite ? '?overwrite=true' : '';
  const res = await fetch(`/api/users/me/browser-macros/compile${qs}`, {
    method: 'POST',
    credentials: 'include',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(rest),
  });
  if (!res.ok) {
    const data = await res.json().catch(() => ({}));
    const err = new Error((data as { error?: string }).error ?? `HTTP ${res.status}`);
    (err as any).status = res.status;
    throw err;
  }
  return res.json();
}

function emptyHint(): ParamHint {
  return { name: '', valueToReplace: '', type: 'string' };
}

export function SaveAsScriptDialog({ recordingName, onClose, onSuccess }: SaveAsScriptDialogProps) {
  const qc = useQueryClient();
  const [scriptName, setScriptName] = useState(recordingName);
  const [description, setDescription] = useState('');
  const [sessionProfileId, setSessionProfileId] = useState('');
  const [paramHints, setParamHints] = useState<ParamHint[]>([]);
  const [overwrite, setOverwrite] = useState(false);
  const [validationError, setValidationError] = useState<string | null>(null);
  const [conflictError, setConflictError] = useState<string | null>(null);
  const backdrop = useBackdropClose(onClose);

  const { data: sessionProfiles = [], isLoading: profilesLoading } = useQuery({
    queryKey: ['browser-session-profiles'],
    queryFn: listBrowserSessionProfiles,
    staleTime: 60 * 1000,
  });
  const activeSessionProfiles = sessionProfiles.filter(p => p.status === 'active');

  const compileMutation = useMutation({
    mutationFn: apiCompileScript,
    onSuccess: (data) => {
      // Invalidate browser-macros list so the new file appears
      qc.invalidateQueries({ queryKey: ['userfolder', 'list', 'browser-macros'] });
      onSuccess(data.scriptName);
    },
    onError: (err: any) => {
      if (err.status === 409) {
        setConflictError('Script already exists; check the overwrite box and retry.');
      }
      // Other errors surface via compileMutation.error
    },
  });

  function validate(): boolean {
    if (!scriptName.trim()) {
      setValidationError('Script name is required.');
      return false;
    }
    const nameWithoutExt = scriptName.endsWith('.js') ? scriptName.slice(0, -3) : scriptName;
    if (!SCRIPT_NAME_RE.test(nameWithoutExt)) {
      setValidationError('Script name may only contain letters, numbers, dashes, underscores, and dots.');
      return false;
    }
    if (!description.trim()) {
      setValidationError('Description is required.');
      return false;
    }
    for (let i = 0; i < paramHints.length; i++) {
      const h = paramHints[i]!;
      if (!h.name.trim() || !h.valueToReplace.trim()) {
        setValidationError(`Param hint #${i + 1} must have a name and value to replace.`);
        return false;
      }
    }
    setValidationError(null);
    setConflictError(null);
    return true;
  }

  function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    if (!validate()) return;

    const profileId = sessionProfileId.trim() !== '' ? parseInt(sessionProfileId, 10) : undefined;
    compileMutation.mutate({
      recordingName,
      scriptName: scriptName.trim(),
      description: description.trim(),
      sessionProfileId: profileId !== undefined && !isNaN(profileId) ? profileId : undefined,
      paramHints: paramHints.length > 0 ? paramHints : undefined,
      overwrite,
    });
  }

  function addHint() {
    setParamHints(prev => [...prev, emptyHint()]);
  }

  function updateHint(idx: number, patch: Partial<ParamHint>) {
    setParamHints(prev => prev.map((h, i) => i === idx ? { ...h, ...patch } : h));
  }

  function removeHint(idx: number) {
    setParamHints(prev => prev.filter((_, i) => i !== idx));
  }

  const isSubmitting = compileMutation.isPending;
  const submitError = compileMutation.isError && !conflictError
    ? ((compileMutation.error as any)?.message ?? 'Compile failed')
    : null;

  return (
    /* Backdrop */
    <div
      className="fixed inset-0 z-50 flex items-center justify-center bg-black/40"
      {...backdrop}
    >
      <div className="bg-surface rounded-xl shadow-xl w-full max-w-lg mx-4 overflow-hidden flex flex-col max-h-[90vh]">
        {/* Header */}
        <div className="flex items-center gap-3 px-5 py-4 border-b border-hairline">
          <span className="text-sm font-semibold text-slate-800 flex-1">Save as Script</span>
          <button
            type="button"
            onClick={onClose}
            className="w-6 h-6 flex items-center justify-center rounded hover:bg-surface-2 text-slate-400 hover:text-slate-700 transition-colors"
            aria-label="Close"
          >
            <svg viewBox="0 0 16 16" className="w-3.5 h-3.5" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
              <path d="M3 3l10 10M13 3L3 13" />
            </svg>
          </button>
        </div>

        {/* Form */}
        <form onSubmit={handleSubmit} className="flex flex-col gap-4 px-5 py-4 overflow-y-auto flex-1">
          {/* Recording name (read-only) */}
          <div className="flex flex-col gap-1">
            <label className="text-2xs font-semibold text-slate-500 uppercase tracking-wide">
              Recording
            </label>
            <div className="px-3 py-2 rounded-md bg-surface-2 text-xs font-mono text-slate-700 border border-hairline">
              {recordingName}.json
            </div>
          </div>

          {/* Script name */}
          <div className="flex flex-col gap-1">
            <label htmlFor="sas-script-name" className="text-2xs font-semibold text-slate-500 uppercase tracking-wide">
              Script name <span className="text-red-500">*</span>
            </label>
            <input
              id="sas-script-name"
              type="text"
              value={scriptName}
              onChange={e => setScriptName(e.target.value)}
              placeholder="my-script"
              className="px-3 py-2 rounded-md border border-hairline text-[13px] font-mono focus:outline-none focus:ring-2 focus:ring-accent/30 focus:border-accent"
            />
            <span className="text-[10px] text-slate-400">Alphanumeric, dashes, underscores, dots. A .js extension will be added automatically.</span>
          </div>

          {/* Description */}
          <div className="flex flex-col gap-1">
            <label htmlFor="sas-description" className="text-2xs font-semibold text-slate-500 uppercase tracking-wide">
              Description <span className="text-red-500">*</span>
            </label>
            <textarea
              id="sas-description"
              value={description}
              onChange={e => setDescription(e.target.value)}
              rows={3}
              placeholder="What does this script do?"
              className="px-3 py-2 rounded-md border border-hairline text-[13px] focus:outline-none focus:ring-2 focus:ring-accent/30 focus:border-accent resize-none"
            />
          </div>

          {/* Session profile */}
          <div className="flex flex-col gap-1">
            <label htmlFor="sas-session-profile" className="text-2xs font-semibold text-slate-500 uppercase tracking-wide">
              Session profile <span className="text-slate-400 font-normal">(optional)</span>
            </label>
            {profilesLoading ? (
              <div className="px-3 py-2 text-xs text-slate-500">Loading profiles…</div>
            ) : activeSessionProfiles.length === 0 ? (
              <div className="px-3 py-2 text-xs text-slate-500">
                No active session profiles. Create one in the Browser tab to enable authenticated automation.
              </div>
            ) : (
              <select
                id="sas-session-profile"
                value={sessionProfileId}
                onChange={e => setSessionProfileId(e.target.value)}
                className="px-3 py-2 rounded-md border border-hairline text-[13px] focus:outline-none focus:ring-2 focus:ring-accent/30 focus:border-accent"
              >
                <option value="">None</option>
                {activeSessionProfiles.map(p => (
                  <option key={p.id} value={String(p.id)}>{p.label} (#{p.id})</option>
                ))}
              </select>
            )}
          </div>

          {/* Param hints */}
          <div className="flex flex-col gap-2">
            <div className="flex items-center gap-2">
              <span className="text-2xs font-semibold text-slate-500 uppercase tracking-wide flex-1">
                Param hints <span className="text-slate-400 font-normal">(optional)</span>
              </span>
              <button
                type="button"
                onClick={addHint}
                className="text-2xs px-2 py-1 rounded border border-hairline text-slate-600 hover:bg-surface-2 transition-colors"
              >
                + Add hint
              </button>
            </div>

            {paramHints.map((hint, idx) => (
              <div key={idx} className="flex gap-2 items-start p-2 rounded-md border border-hairline bg-surface-2">
                <div className="flex flex-col gap-1 flex-1 min-w-0">
                  <input
                    type="text"
                    value={hint.name}
                    onChange={e => updateHint(idx, { name: e.target.value })}
                    placeholder="param name"
                    className="px-2 py-1 rounded border border-hairline text-2xs font-mono bg-canvas focus:outline-none focus:ring-1 focus:ring-accent/30"
                  />
                  <input
                    type="text"
                    value={hint.valueToReplace}
                    onChange={e => updateHint(idx, { valueToReplace: e.target.value })}
                    placeholder="value to replace (literal)"
                    className="px-2 py-1 rounded border border-hairline text-2xs font-mono bg-canvas focus:outline-none focus:ring-1 focus:ring-accent/30"
                  />
                  <select
                    value={hint.type}
                    onChange={e => updateHint(idx, { type: e.target.value as ParamHint['type'] })}
                    className="px-2 py-1 rounded border border-hairline text-2xs bg-canvas focus:outline-none focus:ring-1 focus:ring-accent/30"
                  >
                    <option value="string">string</option>
                    <option value="number">number</option>
                    <option value="boolean">boolean</option>
                  </select>
                </div>
                <button
                  type="button"
                  onClick={() => removeHint(idx)}
                  aria-label="Remove hint"
                  className="mt-1 w-5 h-5 flex-shrink-0 flex items-center justify-center rounded hover:bg-red-100 dark:hover:bg-red-500/15 hover:text-red-600 text-slate-400 transition-colors"
                >
                  <svg viewBox="0 0 16 16" className="w-3 h-3" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
                    <path d="M4 4l8 8M12 4l-8 8" />
                  </svg>
                </button>
              </div>
            ))}
          </div>

          {/* Overwrite checkbox */}
          <label className="flex items-center gap-2 cursor-pointer select-none">
            <input
              type="checkbox"
              checked={overwrite}
              onChange={e => { setOverwrite(e.target.checked); setConflictError(null); }}
              className="rounded border-hairline accent-accent"
            />
            <span className="text-xs text-slate-700">Overwrite if script already exists</span>
          </label>

          {/* Errors */}
          {(validationError || conflictError || submitError) && (
            <div className="px-3 py-2 rounded-md bg-red-50 dark:bg-red-500/15 border border-red-200 dark:border-red-500/30 text-xs text-red-700 dark:text-red-300">
              {validationError ?? conflictError ?? submitError}
            </div>
          )}
        </form>

        {/* Footer */}
        <div className="flex items-center justify-end gap-2 px-5 py-3 border-t border-hairline bg-surface-2/50">
          <button
            type="button"
            onClick={onClose}
            disabled={isSubmitting}
            className="px-4 py-1.5 rounded-md text-xs font-medium text-slate-700 hover:bg-surface-2 disabled:opacity-50 transition-colors border border-hairline"
          >
            Cancel
          </button>
          <button
            type="submit"
            form=""
            onClick={handleSubmit}
            disabled={isSubmitting}
            className="px-4 py-1.5 rounded-md text-xs font-semibold bg-accent text-accent-fg hover:bg-accent-deep disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
          >
            {isSubmitting ? 'Compiling…' : 'Save as Script'}
          </button>
        </div>
      </div>
    </div>
  );
}
