import { useCallback, useEffect, useRef, useState, type ReactNode } from 'react';
import { ArrowLeft, Check, Copy, Image as ImageIcon, Plus, Trash2, TriangleAlert, Upload } from 'lucide-react';
import { navigateToPath } from '../routing';
import { usePrimaryAction } from '../primaryAction';
import { Badge, BugPreview, Button, Card, Checkbox, ConfirmDialog, IconButton, Input, Select, Spinner } from '../components';
import {
  createWatermark,
  deleteWatermark,
  getWatermark,
  getWatermarks,
  messageFromWatermarkError,
  updateWatermark,
  uploadArtwork,
  watermarkImageUrl,
  type CreateWatermarkRequest,
  type Watermark
} from '../api';

const BASE_PATH = '/app/watermarks';

type Draft = CreateWatermarkRequest;

const MODE_OPTIONS = [
  { label: 'None', value: 'None' },
  { label: 'Permanent', value: 'Permanent' },
  { label: 'Intermittent', value: 'Intermittent' },
  { label: 'Opacity Expression', value: 'OpacityExpression' }
];

const IMAGE_SOURCE_OPTIONS = [
  { label: 'Custom', value: 'Custom' },
  { label: 'Channel Logo', value: 'ChannelLogo' }
];

// Blazor location order (WatermarkEditor.razor); MiddleCenter is intentionally not offered.
const LOCATION_OPTIONS = [
  { label: 'Bottom Right', value: 'BottomRight' },
  { label: 'Bottom Middle', value: 'BottomMiddle' },
  { label: 'Bottom Left', value: 'BottomLeft' },
  { label: 'Left Middle', value: 'LeftMiddle' },
  { label: 'Top Left', value: 'TopLeft' },
  { label: 'Top Middle', value: 'TopMiddle' },
  { label: 'Top Right', value: 'TopRight' },
  { label: 'Right Middle', value: 'RightMiddle' }
];

const SIZE_OPTIONS = [
  { label: 'Scaled', value: 'Scaled' },
  { label: 'Actual Size', value: 'ActualSize' }
];

const FREQUENCY_OPTIONS = [5, 10, 15, 20, 30, 60].map((minutes) => ({
  label: `${minutes} minutes`,
  value: String(minutes)
}));

// Mirrors WatermarkEditViewModelValidator. Returns the first blocking message, or null.
function validate(draft: Draft): null | string {
  if (!draft.name.trim()) {
    return 'Name is required';
  }

  if (draft.imageSource === 'Custom' && !draft.image) {
    return 'Watermark image is required';
  }

  if (draft.mode === 'None') {
    return null;
  }

  if (draft.size === 'Scaled' && (draft.width <= 0 || draft.width > 100)) {
    return 'Width must be between 0 and 100';
  }

  if (draft.horizontalMargin < 0 || draft.horizontalMargin > 50) {
    return 'Horizontal margin must be between 0 and 50';
  }

  if (draft.verticalMargin < 0 || draft.verticalMargin > 50) {
    return 'Vertical margin must be between 0 and 50';
  }

  if (draft.durationSeconds <= 0 || draft.durationSeconds >= draft.frequencyMinutes * 60) {
    return 'Duration must be greater than 0 and less than the frequency';
  }

  if (draft.opacity <= 0 || draft.opacity > 100) {
    return 'Opacity must be between 0 and 100';
  }

  return null;
}

function defaultDraft(): Draft {
  return {
    durationSeconds: 15,
    frequencyMinutes: 15,
    horizontalMargin: 5,
    image: null,
    imageContentType: null,
    imageSource: 'Custom',
    location: 'BottomRight',
    mode: 'Permanent',
    name: '',
    opacity: 100,
    opacityExpression: null,
    placeWithinSourceContent: false,
    size: 'Scaled',
    verticalMargin: 5,
    width: 15,
    zIndex: 0
  };
}

function draftFromWatermark(watermark: Watermark & Draft): Draft {
  return {
    durationSeconds: watermark.durationSeconds,
    frequencyMinutes: watermark.frequencyMinutes,
    horizontalMargin: watermark.horizontalMargin,
    image: watermark.image,
    imageContentType: watermark.imageContentType,
    imageSource: watermark.imageSource,
    location: watermark.location,
    mode: watermark.mode,
    name: watermark.name,
    opacity: watermark.opacity,
    opacityExpression: watermark.opacityExpression,
    placeWithinSourceContent: watermark.placeWithinSourceContent,
    size: watermark.size,
    verticalMargin: watermark.verticalMargin,
    width: watermark.width,
    zIndex: watermark.zIndex
  };
}

function Row({
  children,
  control = 320,
  first = false,
  help,
  label
}: {
  children: ReactNode;
  control?: number;
  first?: boolean;
  help?: string;
  label: string;
}) {
  return (
    <div className="ctv-settings-row" style={first ? { borderTop: 'none' } : undefined}>
      <div className="ctv-settings-row-main">
        <div className="ctv-settings-row-label">{label}</div>
        {help && <div className="ctv-settings-row-help">{help}</div>}
      </div>
      <div className="ctv-settings-row-control" style={{ flex: `0 0 ${control}px` }}>{children}</div>
    </div>
  );
}

type Mode = { id: number; kind: 'edit' } | { kind: 'add'; from: null | number } | { kind: 'list' };

// `path` is the screen's pathname state (pathname + search), so mode derives
// from the same string React re-renders on — not the mutable window.location.
function modeFromPath(path: string): Mode {
  const [pathPart, searchPart = ''] = path.split('?');
  const normalized = pathPart.replace(/\/+$/, '');

  if (normalized === BASE_PATH || !normalized.startsWith(`${BASE_PATH}/`)) {
    return { kind: 'list' };
  }

  const suffix = normalized.slice(BASE_PATH.length + 1).split('/')[0];

  if (suffix === 'add') {
    const from = new URLSearchParams(searchPart).get('from');
    return { from: from ? Number(from) : null, kind: 'add' };
  }

  const id = Number(suffix);
  return Number.isInteger(id) && id > 0 ? { id, kind: 'edit' } : { kind: 'list' };
}

export function WatermarksScreen() {
  const [pathname, setPathname] = useState(() => window.location.pathname + window.location.search);

  useEffect(() => {
    const onPop = () => setPathname(window.location.pathname + window.location.search);
    window.addEventListener('popstate', onPop);
    return () => window.removeEventListener('popstate', onPop);
  }, []);

  // TopBar "Add Watermark" primary action → the add editor route.
  usePrimaryAction('watermarks', () => navigateToPath(`${BASE_PATH}/add`));

  const mode = modeFromPath(pathname);

  if (mode.kind === 'list') {
    return <WatermarkList key={pathname} />;
  }

  return <WatermarkEditor key={pathname} mode={mode} />;
}

function WatermarkList() {
  const [watermarks, setWatermarks] = useState<Watermark[] | null>(null);
  const [error, setError] = useState<null | string>(null);
  const [deleteTarget, setDeleteTarget] = useState<Watermark | null>(null);
  const [busy, setBusy] = useState(false);
  const activeRef = useRef(true);

  const load = useCallback(() => {
    getWatermarks()
      .then((data) => {
        if (activeRef.current) {
          setWatermarks(data);
          setError(null);
        }
      })
      .catch((loadError: unknown) => {
        if (activeRef.current) {
          setError(messageFromWatermarkError(loadError));
        }
      });
  }, []);

  useEffect(() => {
    activeRef.current = true;
    load();
    return () => {
      activeRef.current = false;
    };
  }, [load]);

  const confirmDelete = async () => {
    if (!deleteTarget) {
      return;
    }

    setBusy(true);

    try {
      await deleteWatermark(deleteTarget.id);
      setDeleteTarget(null);
      load();
    } catch (deleteError) {
      setError(messageFromWatermarkError(deleteError, 'Unable to delete watermark'));
    } finally {
      setBusy(false);
    }
  };

  if (error && !watermarks) {
    return (
      <div className="ctv-collections">
        <div className="ctv-channels-error" role="alert">
          <TriangleAlert aria-hidden="true" size={15} />
          <span>{error}</span>
          <span className="ctv-channels-spacer" />
          <Button onClick={load} size="sm" variant="secondary">
            Retry
          </Button>
        </div>
      </div>
    );
  }

  if (!watermarks) {
    return (
      <div className="ctv-collections-loading" role="status">
        <Spinner size={18} />
        <span>Loading watermarks…</span>
      </div>
    );
  }

  const sorted = [...watermarks].sort((a, b) => (a.name ?? '').localeCompare(b.name ?? ''));

  return (
    <div className="ctv-collections">
      <div className="ctv-channels-actionbar">
        <Badge tone="neutral">{watermarks.length} watermarks</Badge>
        <span className="ctv-channels-spacer" />
        <Button
          onClick={() => navigateToPath(`${BASE_PATH}/add`)}
          size="sm"
          startIcon={<Plus aria-hidden="true" size={14} />}
        >
          New watermark
        </Button>
      </div>

      {error && (
        <div className="ctv-channels-error" role="alert">
          <TriangleAlert aria-hidden="true" size={15} />
          <span>{error}</span>
        </div>
      )}

      <Card padded={false}>
        {sorted.length === 0 ? (
          <div className="ctv-collections-empty">No watermarks yet.</div>
        ) : (
          sorted.map((watermark, index) => (
            <div
              className="ctv-settings-flush-row"
              key={watermark.id}
              style={index === 0 ? { borderTop: 'none' } : undefined}
            >
              <ImageIcon aria-hidden="true" color="var(--ctv-accent)" size={15} />
              <button
                className="ctv-settings-flush-row-main ctv-settings-flush-row-title ctv-collections-linkbtn"
                onClick={() => navigateToPath(`${BASE_PATH}/${watermark.id}`)}
                type="button"
              >
                {watermark.name}
              </button>
              <IconButton
                onClick={() => navigateToPath(`${BASE_PATH}/add?from=${watermark.id}`)}
                size="sm"
                title={`Copy ${watermark.name}`}
                variant="ghost"
              >
                <Copy aria-hidden="true" size={14} />
              </IconButton>
              <IconButton onClick={() => setDeleteTarget(watermark)} size="sm" title="Delete" variant="ghost">
                <Trash2 aria-hidden="true" size={14} />
              </IconButton>
            </div>
          ))
        )}
      </Card>

      <ConfirmDialog
        busy={busy}
        confirmLabel="Delete"
        message={deleteTarget ? `Delete "${deleteTarget.name}"? This cannot be undone.` : ''}
        onCancel={() => setDeleteTarget(null)}
        onConfirm={() => void confirmDelete()}
        open={deleteTarget !== null}
        title="Delete watermark"
        tone="danger"
      />
    </div>
  );
}

function WatermarkEditor({ mode }: { mode: { id: number; kind: 'edit' } | { from: null | number; kind: 'add' } }) {
  const [draft, setDraft] = useState<Draft | null>(mode.kind === 'add' && mode.from == null ? defaultDraft : null);
  const [loadError, setLoadError] = useState<null | string>(null);
  const [saveError, setSaveError] = useState<null | string>(null);
  const [uploadError, setUploadError] = useState<null | string>(null);
  const [saving, setSaving] = useState(false);
  const [uploading, setUploading] = useState(false);
  const fileRef = useRef<HTMLInputElement>(null);

  const isEdit = mode.kind === 'edit';

  // Captured once at mount; the screen keys this editor by pathname (see ChannelEditScreen).
  const [loadEditId] = useState(() => (mode.kind === 'edit' ? mode.id : null));
  const [loadCopyFrom] = useState(() => (mode.kind === 'add' ? mode.from : null));

  useEffect(() => {
    if (loadEditId == null && loadCopyFrom == null) {
      return;
    }

    let active = true;

    const sourceId = loadEditId ?? loadCopyFrom;

    getWatermark(sourceId as number)
      .then((watermark) => {
        if (active) {
          const source = draftFromWatermark(watermark as Watermark & Draft);
          setDraft(loadEditId != null ? source : { ...source, name: `${source.name} (copy)` });
        }
      })
      .catch((error: unknown) => {
        if (active) {
          setLoadError(messageFromWatermarkError(error, 'Unable to load watermark'));
        }
      });

    return () => {
      active = false;
    };
  }, [loadEditId, loadCopyFrom]);

  if (loadError) {
    return (
      <div className="ctv-settings-error" role="alert">
        <TriangleAlert aria-hidden="true" size={16} />
        <span>{loadError}</span>
        <Button onClick={() => navigateToPath(BASE_PATH)} size="sm" variant="secondary">
          Back to watermarks
        </Button>
      </div>
    );
  }

  if (!draft) {
    return (
      <div className="ctv-settings-loading">
        <Spinner size={18} />
        <span>Loading watermark…</span>
      </div>
    );
  }

  const set = (patch: Partial<Draft>) => {
    setDraft((current) => (current ? { ...current, ...patch } : current));
  };

  const validationError = validate(draft);
  const modeNone = draft.mode === 'None';
  const custom = draft.imageSource === 'Custom';

  const upload = async (file: File) => {
    if (!file.type.startsWith('image/')) {
      setUploadError('Choose an image file.');
      return;
    }

    setUploading(true);
    setUploadError(null);

    try {
      const uploaded = await uploadArtwork(file, 'watermark');
      set({ image: uploaded.path, imageContentType: uploaded.contentType });
    } catch (error) {
      setUploadError(messageFromWatermarkError(error, 'Unable to upload image'));
    } finally {
      setUploading(false);
    }
  };

  const save = async () => {
    if (validationError || saving) {
      return;
    }

    setSaving(true);
    setSaveError(null);

    try {
      if (mode.kind === 'edit') {
        await updateWatermark(mode.id, draft);
      } else {
        await createWatermark(draft);
      }

      navigateToPath(BASE_PATH);
    } catch (error) {
      setSaveError(messageFromWatermarkError(error, 'Unable to save watermark'));
    } finally {
      setSaving(false);
    }
  };

  return (
    <div className="ctv-collections">
      <div className="ctv-channels-actionbar">
        <Button
          onClick={() => navigateToPath(BASE_PATH)}
          size="sm"
          startIcon={<ArrowLeft aria-hidden="true" size={14} />}
          variant="ghost"
        >
          All watermarks
        </Button>
        <span className="ctv-collections-detail-title">{isEdit ? 'Edit watermark' : 'New watermark'}</span>
        <span className="ctv-channels-spacer" />
        {saveError && <span className="ctv-settings-savebar-error">{saveError}</span>}
        {validationError && <Badge tone="neutral">{validationError}</Badge>}
        <Button
          disabled={validationError !== null || saving}
          loading={saving}
          onClick={() => void save()}
          size="sm"
          startIcon={<Check aria-hidden="true" size={14} />}
        >
          {isEdit ? 'Save watermark' : 'Add watermark'}
        </Button>
      </div>

      <Card padded={false} title="Watermark">
        <Row control={360} first label="Name">
          <Input
            error={draft.name.trim() ? null : 'Name is required'}
            onChange={(event) => set({ name: event.target.value })}
            size="sm"
            value={draft.name}
          />
        </Row>
        <Row control={360} label="Mode">
          <Select
            onChange={(event) => set({ mode: event.target.value as Draft['mode'] })}
            options={MODE_OPTIONS}
            value={draft.mode}
          />
        </Row>
        <Row control={360} label="Image source">
          <Select
            disabled={modeNone}
            onChange={(event) => set({ imageSource: event.target.value as Draft['imageSource'] })}
            options={IMAGE_SOURCE_OPTIONS}
            value={draft.imageSource}
          />
        </Row>
        <Row control={360} help="Only used with a custom image source." label="Image">
          <input
            accept="image/*"
            onChange={(event) => {
              const file = event.target.files?.[0];
              if (file) {
                void upload(file);
              }
            }}
            ref={fileRef}
            style={{ display: 'none' }}
            type="file"
          />
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            <Button
              disabled={!custom}
              loading={uploading}
              onClick={() => fileRef.current?.click()}
              size="sm"
              startIcon={<Upload aria-hidden="true" size={14} />}
              variant="secondary"
            >
              Upload image
            </Button>
            {custom && draft.image && (
              <BugPreview
                horizontalMargin={draft.horizontalMargin}
                location={draft.location}
                opacity={draft.opacity}
                size={draft.size}
                src={watermarkImageUrl(draft.image)}
                verticalMargin={draft.verticalMargin}
                width={draft.width}
              />
            )}
            {uploadError && (
              <span className="ctv-field-error" role="alert">
                {uploadError}
              </span>
            )}
          </div>
        </Row>
        <Row control={360} label="Location">
          <Select
            disabled={modeNone}
            onChange={(event) => set({ location: event.target.value as Draft['location'] })}
            options={LOCATION_OPTIONS}
            value={draft.location}
          />
        </Row>
        <Row control={200} label="Place within source content">
          <Checkbox
            checked={draft.placeWithinSourceContent}
            disabled={modeNone}
            onChange={(placeWithinSourceContent) => set({ placeWithinSourceContent })}
          />
        </Row>
        <Row control={360} label="Size">
          <Select
            disabled={modeNone}
            onChange={(event) => set({ size: event.target.value as Draft['size'] })}
            options={SIZE_OPTIONS}
            value={draft.size}
          />
        </Row>
        <Row control={220} label="Width">
          <Input
            disabled={modeNone || draft.size === 'ActualSize'}
            onChange={(event) => set({ width: Number(event.target.value) })}
            size="sm"
            trailing="%"
            type="number"
            value={String(draft.width)}
          />
        </Row>
        <Row control={220} label="Horizontal margin">
          <Input
            disabled={modeNone}
            onChange={(event) => set({ horizontalMargin: Number(event.target.value) })}
            size="sm"
            trailing="%"
            type="number"
            value={String(draft.horizontalMargin)}
          />
        </Row>
        <Row control={220} label="Vertical margin">
          <Input
            disabled={modeNone}
            onChange={(event) => set({ verticalMargin: Number(event.target.value) })}
            size="sm"
            trailing="%"
            type="number"
            value={String(draft.verticalMargin)}
          />
        </Row>
        <Row control={360} label="Frequency">
          <Select
            disabled={draft.mode !== 'Intermittent'}
            onChange={(event) => set({ frequencyMinutes: Number(event.target.value) })}
            options={FREQUENCY_OPTIONS}
            value={String(draft.frequencyMinutes)}
          />
        </Row>
        <Row control={220} label="Duration">
          <Input
            disabled={draft.mode !== 'Intermittent'}
            onChange={(event) => set({ durationSeconds: Number(event.target.value) })}
            size="sm"
            trailing="seconds"
            type="number"
            value={String(draft.durationSeconds)}
          />
        </Row>
        <Row control={220} label="Opacity">
          <Input
            disabled={modeNone || draft.mode === 'OpacityExpression'}
            onChange={(event) => set({ opacity: Number(event.target.value) })}
            size="sm"
            trailing="%"
            type="number"
            value={String(draft.opacity)}
          />
        </Row>
        <Row control={360} label="Opacity expression">
          <Input
            disabled={draft.mode !== 'OpacityExpression'}
            onChange={(event) => set({ opacityExpression: event.target.value })}
            size="sm"
            value={draft.opacityExpression ?? ''}
          />
        </Row>
        <Row control={200} label="Z-index">
          <Input
            onChange={(event) => set({ zIndex: Number(event.target.value) })}
            size="sm"
            type="number"
            value={String(draft.zIndex)}
          />
        </Row>
      </Card>
    </div>
  );
}
