import { useState } from 'react';
import { Search } from 'lucide-react';
import { Badge, Button, Dialog, Input, Spinner } from '../components';
import { getLibraryBrowseItems, messageFromCollectionError, type LibraryBrowseItem } from '../api';
import { TYPE_LABEL } from '../media/mediaKinds';
import { compile } from './rules/compile';
import { useSearchFields } from './rules/fieldCatalog';
import { parse } from './rules/parse';
import { RuleBuilder } from './rules/RuleBuilder';
import type { Group } from './rules/types';
import { groupHasErrors } from './rules/validation';

// The smart-collection create/edit dialog: a name + a Lucene query authored either visually
// (RuleBuilder over the shared `search/fields` catalog) or as raw text. Extracted from
// CollectionsScreen so it can be embedded by other screens that need inline query authoring
// (the Channel Builder's "New smart query", #437) without duplicating the builder/preview
// wiring — see spa-conventions.md §12. The parent owns busy/error and the `onSubmit({name,
// query})` side effect (persist, add to a lineup, …); this component is otherwise self-contained.
export function SmartCollectionDialog({
  busy,
  error,
  initial,
  onCancel,
  onSubmit,
  open
}: {
  busy: boolean;
  error: string | null;
  initial: { name: string; query: string } | null;
  onCancel: () => void;
  onSubmit: (values: { name: string; query: string }) => void;
  open: boolean;
}) {
  // Parent remounts this via `key` on each open, so `initial` seeds fresh state.
  const [name, setName] = useState(initial?.name ?? '');
  const [query, setQuery] = useState(initial?.query ?? '');
  const [preview, setPreview] = useState<{ count: number; sample: LibraryBrowseItem[] } | null>(null);
  const [previewing, setPreviewing] = useState(false);
  const [previewError, setPreviewError] = useState<string | null>(null);

  const { fields, fieldTypes, status: fieldsStatus } = useSearchFields();
  const [mode, setMode] = useState<'builder' | 'advanced'>(() =>
    initial?.query && !parse(initial.query, fieldTypes) ? 'advanced' : 'builder'
  );
  const [group, setGroup] = useState<Group>(
    () => parse(initial?.query ?? '', fieldTypes) ?? { match: 'all', children: [] }
  );

  // `fieldTypes` is empty on first render (catalog still loading), so the useState initializers
  // above see it empty and `parse` returns null — the dialog opens in advanced mode until the
  // catalog arrives. Re-seed once `fieldsStatus` flips to 'success', using React's documented
  // "adjust state during render" pattern (comparing against a previous-render value held in state)
  // instead of a `useEffect` — a synchronous `setState` in an effect body trips
  // react-hooks/set-state-in-effect, and reading a ref during render trips react-hooks/refs.
  const [seededFieldsStatus, setSeededFieldsStatus] = useState(fieldsStatus);
  if (fieldsStatus !== seededFieldsStatus) {
    setSeededFieldsStatus(fieldsStatus);
    if (fieldsStatus === 'success' && initial?.query) {
      const parsed = parse(initial.query, fieldTypes);
      if (parsed) {
        setGroup(parsed);
        setQuery(compile(parsed));
        setMode('builder');
      } else {
        setMode('advanced');
      }
    }
  }

  // Builder-mode edits flow through this handler (a React event handler, not an effect) so the
  // compiled string is always what `query` — the single value submitted/previewed — holds.
  const handleGroupChange = (next: Group) => {
    setGroup(next);
    // Skip compiling an errored tree — a partial rule can still compile to a non-empty (but
    // meaningless) query, which would otherwise satisfy the Save button's non-empty check.
    if (!groupHasErrors(next)) {
      setQuery(compile(next));
    }
  };

  const runPreview = async () => {
    const trimmed = query.trim();

    if (!trimmed) {
      return;
    }

    setPreviewing(true);
    setPreviewError(null);

    try {
      const result = await getLibraryBrowseItems({ pageSize: 24, query: trimmed });
      setPreview({ count: result.totalCount ?? result.page?.length ?? 0, sample: result.page ?? [] });
    } catch (error) {
      setPreviewError(messageFromCollectionError(error, 'Unable to preview query'));
    } finally {
      setPreviewing(false);
    }
  };

  const trimmedName = name.trim();
  const trimmedQuery = query.trim();
  const builderUnavailable = query.trim().length > 0 && parse(query, fieldTypes) === null;
  const groupInvalid = mode === 'builder' && groupHasErrors(group);

  return (
    <Dialog
      footer={
        <>
          <Button disabled={busy} onClick={onCancel} variant="secondary">
            Cancel
          </Button>
          <Button
            disabled={busy || trimmedName.length === 0 || trimmedQuery.length === 0 || groupInvalid}
            loading={busy}
            onClick={() => onSubmit({ name: trimmedName, query: trimmedQuery })}
            variant="primary"
          >
            {initial ? 'Save' : 'Create'}
          </Button>
        </>
      }
      onClose={onCancel}
      open={open}
      title={initial ? 'Edit smart collection' : 'New smart collection'}
      width={560}
    >
      <Input label="Name" onChange={(event) => setName(event.target.value)} placeholder="Smart collection name" value={name} />

      <div style={{ display: 'flex', gap: 8, marginTop: 12, marginBottom: 8 }}>
        <Button
          disabled={builderUnavailable}
          onClick={() => {
            const parsed = parse(query, fieldTypes);
            if (parsed) {
              setGroup(parsed);
              setQuery(compile(parsed));
              setMode('builder');
            }
          }}
          size="sm"
          variant={mode === 'builder' ? 'primary' : 'secondary'}
        >
          Builder
        </Button>
        <Button
          onClick={() => {
            if (mode !== 'advanced') {
              setQuery(compile(group));
              setMode('advanced');
            }
          }}
          size="sm"
          variant={mode === 'advanced' ? 'primary' : 'secondary'}
        >
          Advanced
        </Button>
      </div>

      {mode === 'builder' ? (
        fieldsStatus === 'success' ? (
          <RuleBuilder fields={fields} onChange={handleGroupChange} value={group} />
        ) : (
          <Spinner />
        )
      ) : (
        <div>
          <Input
            label="Search query"
            onChange={(event) => setQuery(event.target.value)}
            placeholder='e.g. genre:"action" AND released:2000-2010'
            style={{ fontFamily: 'var(--font-mono)' }}
            value={query}
          />
          {parse(query, fieldTypes) === null && query.trim().length > 0 && (
            <span className="ctv-field-hint">This query is too advanced to show in the builder.</span>
          )}
        </div>
      )}

      <div style={{ display: 'flex', gap: 8, marginTop: 12 }}>
        <Button
          disabled={previewing || trimmedQuery.length === 0}
          loading={previewing}
          onClick={() => void runPreview()}
          size="sm"
          startIcon={<Search aria-hidden="true" size={14} />}
          variant="secondary"
        >
          Preview results
        </Button>
      </div>
      {previewError && (
        <span className="ctv-field-error" role="alert">
          {previewError}
        </span>
      )}
      {preview && (
        <div className="ctv-collections-preview">
          <div className="ctv-collections-preview-head">
            <Badge tone="accent">{preview.count} matches</Badge>
          </div>
          <ul className="ctv-collections-preview-list">
            {preview.sample.slice(0, 12).map((item) => (
              <li key={`${item.mediaType}:${item.id}`}>
                <span className="ctv-collections-preview-title">{item.title}</span>
                <span className="ctv-collections-preview-type">{TYPE_LABEL[item.mediaType]}</span>
              </li>
            ))}
            {preview.sample.length === 0 && <li className="ctv-collections-preview-empty">No matches</li>}
          </ul>
        </div>
      )}
      {error && (
        <span className="ctv-field-error" role="alert">
          {error}
        </span>
      )}
    </Dialog>
  );
}
