/**
 * Datasets manager — the modal body (spec §05).
 *
 * A two-pane manager rendered inside the shared modal shell (App provides the
 * backdrop, header, close, and focus trap). Left: a "New Dataset" action plus the
 * dataset list, newest-modified first, each row carrying a source/rows/format/size
 * meta line and a usage badge. Right: the selected dataset's detail, the
 * create/edit form, or an empty prompt.
 *
 * State lives in DatasetStore; the bidirectional snippet↔dataset link is derived
 * by scanning SnippetStore (docs/architecture/07 §4), so usage counts and Linked
 * Snippets stay reactive without a stored back-pointer.
 */

import { useState } from 'react';
import { useShallow } from 'zustand/react/shallow';
import { datasetReference, type DataSource, type Dataset } from '@core/dataset';
import { detectFormat, detectFormatFromUrl, type DataFormat } from '@core/format-detection';
import { closeModal, resnapshot } from '../modals/ModalCoordinator';
import { confirm } from '../stores/ConfirmStore';
import { notify } from '../stores/NotificationStore';
import { selectSelectedDataset, useDatasetStore, byModifiedDesc } from '../stores/DatasetStore';
import { useSnippetStore } from '../stores/SnippetStore';
import { SegmentedControl, type SegmentedOption } from './SegmentedControl';
import styles from './DatasetsModal.module.css';

/** Display label for a format (spec §05 → List item: JSON / CSV / TSV / TopoJSON). */
function formatLabel(format: DataFormat): string {
  return format === 'topojson' ? 'TopoJSON' : format.toUpperCase();
}

/** Human-readable byte size (B / KB / MB). */
function humanBytes(bytes: number): string {
  if (bytes < 1024) return `${bytes} B`;
  const kb = bytes / 1024;
  if (kb < 1024) return `${kb < 10 ? kb.toFixed(1) : Math.round(kb)} KB`;
  const mb = kb / 1024;
  return `${mb < 10 ? mb.toFixed(1) : Math.round(mb)} MB`;
}

/** Map of dataset name (lower-cased) → how many snippets reference it. */
function usageByName(snippets: ReadonlyArray<{ datasetRefs: string[] }>): Map<string, number> {
  const counts = new Map<string, number>();
  for (const s of snippets) {
    for (const ref of s.datasetRefs) {
      const key = ref.toLowerCase();
      counts.set(key, (counts.get(key) ?? 0) + 1);
    }
  }
  return counts;
}

const SOURCE_OPTIONS: ReadonlyArray<SegmentedOption<DataSource>> = [
  { value: 'inline', label: 'Inline' },
  { value: 'url', label: 'URL' },
];

export function DatasetsModal() {
  const datasets = useDatasetStore(useShallow((s) => s.datasets));
  const view = useDatasetStore((s) => s.view);
  const selected = useDatasetStore(selectSelectedDataset);
  const snippets = useSnippetStore(useShallow((s) => s.snippets));

  const select = useDatasetStore((s) => s.select);
  const startCreate = useDatasetStore((s) => s.startCreate);

  const usage = usageByName(snippets);
  const ordered = [...datasets].sort(byModifiedDesc);

  const handleNew = () => {
    startCreate();
    resnapshot(); // baseline the discard check to the freshly-opened empty form
  };

  return (
    <div className={styles.manager}>
      <div className={styles.listPane}>
        <button type="button" className={styles.newButton} onClick={handleNew}>
          + New Dataset
        </button>
        <ul className={styles.list}>
          {ordered.length === 0 && (
            <li className={styles.empty}>
              No datasets yet — create one to reuse data across snippets.
            </li>
          )}
          {ordered.map((d) => (
            <DatasetListItem
              key={d.id}
              dataset={d}
              active={d.id === selected?.id}
              usage={usage.get(d.name.toLowerCase()) ?? 0}
              onSelect={() => select(d.id)}
            />
          ))}
        </ul>
      </div>

      <div className={styles.detailPane}>
        {view === 'new' || view === 'edit' ? (
          <DatasetFormView editing={view === 'edit'} />
        ) : selected ? (
          <DatasetDetail dataset={selected} snippets={snippets} />
        ) : (
          <div className={styles.detailEmpty}>Select a dataset or create a new one.</div>
        )}
      </div>
    </div>
  );
}

function DatasetListItem({
  dataset,
  active,
  usage,
  onSelect,
}: {
  dataset: Dataset;
  active: boolean;
  usage: number;
  onSelect: () => void;
}) {
  // Meta line: source ("URL" prefix), row count when known, format label, size.
  const parts: string[] = [];
  if (dataset.source === 'url') parts.push('URL');
  if (dataset.rowCount !== null) parts.push(`${dataset.rowCount} rows`);
  parts.push(formatLabel(dataset.format));
  if (dataset.source !== 'url') parts.push(humanBytes(dataset.size));

  return (
    <li className={`${styles.item} ${active ? styles.itemActive : ''}`}>
      <button
        type="button"
        className={styles.itemMain}
        aria-current={active || undefined}
        onClick={onSelect}
      >
        <span className={styles.itemName}>{dataset.name}</span>
        <span className={styles.itemMeta}>{parts.join(' · ')}</span>
      </button>
      {usage > 0 && (
        <span className={styles.badge} title={`Used by ${usage} snippet${usage === 1 ? '' : 's'}`}>
          {usage}
        </span>
      )}
    </li>
  );
}

function DatasetDetail({
  dataset,
  snippets,
}: {
  dataset: Dataset;
  snippets: ReadonlyArray<{ id: string; name: string; datasetRefs: string[] }>;
}) {
  const startEdit = useDatasetStore((s) => s.startEdit);
  const remove = useDatasetStore((s) => s.remove);
  const selectSnippet = useSnippetStore((s) => s.selectSnippet);
  const [copied, setCopied] = useState(false);

  const lower = dataset.name.toLowerCase();
  const linked = snippets.filter((s) => s.datasetRefs.some((r) => r.toLowerCase() === lower));

  const handleEdit = () => {
    startEdit();
    resnapshot();
  };

  const handleCopy = async () => {
    const text = JSON.stringify(datasetReference(dataset.name), null, 2);
    try {
      await navigator.clipboard.writeText(text);
      // Lightweight local feedback; the success-toast wiring is deferred to M6
      // with the other success toasts (spec §05 → Actions).
      setCopied(true);
      setTimeout(() => setCopied(false), 1500);
    } catch {
      notify({
        kind: 'error',
        title: "Couldn't copy",
        message: 'Your browser blocked clipboard access. Select and copy the reference manually.',
      });
    }
  };

  const handleDelete = async () => {
    const ok = await confirm({
      title: 'Delete dataset',
      message: `Delete "${dataset.name}"? This cannot be undone.`,
      confirmLabel: 'Delete',
      danger: true,
    });
    if (!ok) return;
    const removedName = dataset.name;
    remove(dataset.id);
    // Confirm the deletion (spec §05). The message names which dataset went
    // (council toast-copy rule, docs/architecture/10 → Toast copy).
    notify({
      kind: 'success',
      title: 'Dataset deleted',
      message: `"${removedName}" was permanently removed.`,
    });
  };

  return (
    <div className={styles.detail}>
      <div className={styles.detailHead}>
        <h3 className={styles.detailName}>{dataset.name}</h3>
        <div className={styles.detailActions}>
          <button type="button" className={styles.action} onClick={() => void handleCopy()}>
            {copied ? 'Copied' : 'Copy Reference'}
          </button>
          {/* The clipboard write is invisible, so the success is confirmed inline
              ("Copied") rather than by a toast (docs/architecture/10 → Toast copy).
              This polite live region announces it to assistive tech, which the
              button's visual label swap alone would not reliably do. */}
          <span role="status" className="visually-hidden">
            {copied ? 'Reference copied to clipboard' : ''}
          </span>
          <button type="button" className={styles.action} onClick={handleEdit}>
            Edit
          </button>
          {/* "Build Chart from dataset" (spec §05) lands enabled with the Chart
              Builder in M4. Per council (GOV.UK / NN/g), we don't ship a dead
              disabled control in the meantime — the action appears when it works. */}
          <button
            type="button"
            className={`${styles.action} ${styles.danger}`}
            onClick={() => void handleDelete()}
          >
            Delete
          </button>
        </div>
      </div>

      {dataset.comment && <p className={styles.comment}>{dataset.comment}</p>}

      <section className={styles.section}>
        <h4 className={styles.sectionTitle}>Overview</h4>
        <dl className={styles.stats}>
          <div>
            <dt>Rows</dt>
            <dd>{dataset.rowCount ?? 'N/A'}</dd>
          </div>
          <div>
            <dt>Columns</dt>
            <dd>{dataset.columnCount ?? 'N/A'}</dd>
          </div>
          <div>
            <dt>Format</dt>
            <dd>{formatLabel(dataset.format)}</dd>
          </div>
          <div>
            <dt>Size</dt>
            <dd>{dataset.source === 'url' ? 'N/A' : humanBytes(dataset.size)}</dd>
          </div>
        </dl>

        {dataset.columnTypes.length > 0 && (
          <ul className={styles.columns}>
            {dataset.columnTypes.map((col) => (
              <li key={col.name} className={styles.column}>
                <span className={styles.columnName}>{col.name}</span>
                <span className={styles.columnType}>{col.type}</span>
              </li>
            ))}
          </ul>
        )}

        <div className={styles.timestamps}>
          <span>Created {new Date(dataset.created).toLocaleString()}</span>
          <span>Modified {new Date(dataset.modified).toLocaleString()}</span>
        </div>
      </section>

      <section className={styles.section}>
        <h4 className={styles.sectionTitle}>Preview</h4>
        <pre className={styles.preview}>{previewText(dataset)}</pre>
      </section>

      <section className={styles.section}>
        <h4 className={styles.sectionTitle}>Linked Snippets</h4>
        {linked.length === 0 ? (
          <p className={styles.muted}>No snippets reference this dataset yet.</p>
        ) : (
          <ul className={styles.linked}>
            {linked.map((s) => (
              <li key={s.id}>
                <button
                  type="button"
                  className={styles.linkButton}
                  onClick={() => {
                    selectSnippet(s.id);
                    void closeModal();
                  }}
                >
                  {s.name}
                </button>
              </li>
            ))}
          </ul>
        )}
      </section>
    </div>
  );
}

/** A truncated rendering of the data: raw for csv/tsv/url, pretty JSON otherwise. */
function previewText(dataset: Dataset): string {
  const MAX = 2000;
  let text: string;
  if (dataset.source === 'url') {
    text = String(dataset.data);
  } else if (dataset.format === 'csv' || dataset.format === 'tsv') {
    text = String(dataset.data);
  } else {
    try {
      text = JSON.stringify(dataset.data, null, 2);
    } catch {
      text = String(dataset.data);
    }
  }
  return text.length > MAX ? `${text.slice(0, MAX)}\n…` : text;
}

function DatasetFormView({ editing }: { editing: boolean }) {
  const form = useDatasetStore((s) => s.form);
  const formError = useDatasetStore((s) => s.formError);
  const updateForm = useDatasetStore((s) => s.updateForm);
  const cancelForm = useDatasetStore((s) => s.cancelForm);
  const save = useDatasetStore((s) => s.save);

  // Live format/source hint from the current input (spec §05 → Auto-detection).
  const detected =
    form.source === 'url'
      ? { format: detectFormatFromUrl(form.input.trim()), confidence: 'url' as const }
      : detectFormat(form.input);

  const handleSave = () => {
    if (save()) resnapshot(); // committed — re-baseline so a later close won't prompt
  };

  const handleCancel = () => {
    cancelForm();
    resnapshot();
  };

  return (
    <div className={styles.form}>
      <h3 className={styles.detailName}>{editing ? 'Edit dataset' : 'New dataset'}</h3>

      <label className={styles.field}>
        <span className={styles.label}>Name</span>
        <input
          type="text"
          className={styles.input}
          value={form.name}
          onChange={(e) => updateForm({ name: e.target.value })}
          placeholder="e.g. Sales 2024"
        />
      </label>

      <div className={styles.field}>
        <span className={styles.label}>Source</span>
        <SegmentedControl
          label="Dataset source"
          options={SOURCE_OPTIONS}
          value={form.source}
          onChange={(source) => updateForm({ source })}
        />
      </div>

      <label className={styles.field}>
        <span className={styles.label}>{form.source === 'url' ? 'URL' : 'Data'}</span>
        {form.source === 'url' ? (
          <input
            type="url"
            className={styles.input}
            value={form.input}
            onChange={(e) => updateForm({ input: e.target.value })}
            placeholder="https://example.com/data.csv"
          />
        ) : (
          <textarea
            className={styles.textarea}
            value={form.input}
            onChange={(e) => updateForm({ input: e.target.value })}
            placeholder="Paste JSON, CSV, or TSV…"
            rows={10}
            spellCheck={false}
          />
        )}
      </label>

      {form.input.trim() !== '' && (
        <div className={styles.detected}>
          <span className={styles.detectedBadge}>
            {detected.format ? formatLabel(detected.format) : 'Unrecognized'}
          </span>
          {form.source !== 'url' && 'confidence' in detected && (
            <span className={styles.detectedHint}>{detected.confidence} confidence</span>
          )}
        </div>
      )}

      <label className={styles.field}>
        <span className={styles.label}>Comment (optional)</span>
        <input
          type="text"
          className={styles.input}
          value={form.comment}
          onChange={(e) => updateForm({ comment: e.target.value })}
          placeholder="Notes about this dataset"
        />
      </label>

      {formError && (
        <p className={styles.formError} role="alert">
          {formError}
        </p>
      )}

      <div className={styles.formActions}>
        <button type="button" className={styles.action} onClick={handleCancel}>
          Cancel
        </button>
        <button type="button" className={`${styles.action} ${styles.primary}`} onClick={handleSave}>
          {editing ? 'Save changes' : 'Create dataset'}
        </button>
      </div>
    </div>
  );
}
