import { useEffect, useState } from 'react';
import { api } from '../api/client';
import { useAction } from '../lib/hooks';
import { useGateway } from '../lib/gateway';
import { useToast } from '../lib/toast';
import { Banner, Button, Card, Empty, Field, Grid, Loading, PageHead, Tag, Toggle } from '../components/ui';
import type { HeroItem, HeroPlacement, HeroPlacementPolicy, HeroSchedule } from '../api/types';

const MAX_PINS = 4;
const PLACEMENTS: Array<{ id: HeroPlacement; label: string; type: string }> = [
  { id: 'home', label: 'Home', type: 'films and television shows' },
  { id: 'movies', label: 'Movies', type: 'films' },
  { id: 'tv_shows', label: 'TV Shows', type: 'television shows' },
];
const emptyPlacement = (): HeroPlacementPolicy => ({ pinnedItems: [], primeSubtitle: '' });
const WEEKDAYS = [
  { value: 1, short: 'Mon', label: 'Monday' },
  { value: 2, short: 'Tue', label: 'Tuesday' },
  { value: 3, short: 'Wed', label: 'Wednesday' },
  { value: 4, short: 'Thu', label: 'Thursday' },
  { value: 5, short: 'Fri', label: 'Friday' },
  { value: 6, short: 'Sat', label: 'Saturday' },
  { value: 0, short: 'Sun', label: 'Sunday' },
];

type ScheduleFrequency = 'once' | 'daily' | 'weekly';

function localDateTime(value: Date): string {
  const offset = value.getTimezoneOffset() * 60_000;
  return new Date(value.getTime() - offset).toISOString().slice(0, 16);
}

function usableAbsoluteTime(value: string | undefined): boolean {
  return Boolean(value && Number.isFinite(new Date(value).getTime()) && new Date(value).getFullYear() >= 2000);
}

function scheduleFrequency(schedule: HeroSchedule): ScheduleFrequency {
  return schedule.frequency ?? 'once';
}

function scheduleSummary(schedule: HeroSchedule): string {
  if (schedule.frequency === 'daily') {
    return `Every day · ${schedule.startTime}–${schedule.endTime}`;
  }
  if (schedule.frequency === 'weekly') {
    const selected = WEEKDAYS.filter((day) => (schedule.weekdays ?? []).includes(day.value));
    const days = selected.length === 7 ? 'Every day' : selected.map((day) => day.short).join(', ');
    return `${days || 'No days selected'} · ${schedule.startTime}–${schedule.endTime}`;
  }
  return `${schedule.startAt ? new Date(schedule.startAt).toLocaleString() : 'Start missing'} → ${schedule.endAt ? new Date(schedule.endAt).toLocaleString() : 'End missing'}`;
}

function newSchedule(item: HeroItem, placement: HeroPlacement): HeroSchedule {
  const start = new Date();
  start.setMinutes(Math.ceil(start.getMinutes() / 30) * 30, 0, 0);
  const end = new Date(start.getTime() + 2 * 60 * 60 * 1000);
  const validPlacement = placement === 'home' ||
    (placement === 'movies' && item.type === 'Movie') ||
    (placement === 'tv_shows' && item.type === 'Series');
  return {
    id: crypto.randomUUID(), itemId: item.id,
    startAt: start.toISOString(), endAt: end.toISOString(),
    priority: 0, enabled: true, placements: [validPlacement ? placement : 'home'],
  };
}

export function HeroPage() {
  const { status, error, loading, reload } = useGateway();
  const { wrap, show } = useToast();
  const { busy, run } = useAction();
  const [placement, setPlacement] = useState<HeroPlacement>('home');
  const [placements, setPlacements] = useState<Record<HeroPlacement, HeroPlacementPolicy>>({
    home: emptyPlacement(), movies: emptyPlacement(), tv_shows: emptyPlacement(),
  });
  const [dirty, setDirty] = useState(false);
  const [queryText, setQueryText] = useState('');
  const [results, setResults] = useState<HeroItem[] | null>(null);
  const [schedules, setSchedules] = useState<HeroSchedule[]>([]);
  const [scheduleDraft, setScheduleDraft] = useState<HeroSchedule | null>(null);

  const policy = status?.heroPolicy;

  useEffect(() => {
    // The poll must not take an unsaved arrangement away, which is what `dirty` guards.
    if (dirty || !policy) return;
    setPlacements({
      home: policy.placements?.home ?? { pinnedItems: policy.pinnedItems ?? [], primeSubtitle: policy.primeSubtitle ?? '' },
      movies: policy.placements?.movies ?? emptyPlacement(),
      tv_shows: policy.placements?.tv_shows ?? emptyPlacement(),
    });
    setSchedules(policy.schedules ?? []);
  }, [policy, dirty]);

  const search = () =>
    run('search', async () => {
      const needle = queryText.trim();
      if (!needle) return;
      const payload = await wrap(() =>
        api.get<{ items: HeroItem[] | null }>(`/admin/api/hero/search?q=${encodeURIComponent(needle)}`),
      );
      if (payload) setResults(payload.items ?? []);
    });

  const current = placements[placement];
  const pins = current.pinnedItems ?? [];
  const setCurrent = (next: Partial<HeroPlacementPolicy>) => {
    setPlacements((value) => ({ ...value, [placement]: { ...value[placement], ...next } }));
    setDirty(true);
  };

  const add = (item: HeroItem) => {
    if (pins.some((pin) => pin.id === item.id)) return;
    if (pins.length >= MAX_PINS) {
      show('Remove a pinned title before adding another.', 'bad');
      return;
    }
    setCurrent({ pinnedItems: [...pins, item] });
  };

  const save = () =>
    run('save', async () => {
      await wrap(
        () =>
          api.post('/admin/api/hero-policy', {
            placements: Object.fromEntries(Object.entries(placements).map(([name, value]) => [name, {
              pinnedItemIds: (value.pinnedItems ?? []).map((item) => item.id),
              primeSubtitle: value.primeSubtitle.trim(),
            }])),
            schedules,
          }),
        'Hero saved.',
      );
      setDirty(false);
      await reload();
    });

  return (
    <>
      <PageHead
        title="Featured content"
        intro="Manage an independent, backend-resolved hero for Home, Movies and TV Shows."
      />
      <Banner message={error} />

      {loading ? (
        <Loading rows={2} />
      ) : (
        <>
          <Grid>
            {PLACEMENTS.map((option) => {
              const configured = placements[option.id];
              const activeSchedule = schedules
                .filter((entry) => entry.enabled && (entry.placements ?? ['home']).includes(option.id))
                .sort((left, right) => right.priority - left.priority)[0];
              const source = (configured.pinnedItems ?? []).length ? 'Manual' : activeSchedule ? 'Schedule ready' : 'Automatic';
              const scheduledItem = policy?.items?.find((item) => item.id === activeSchedule?.itemId);
              const preview = (configured.pinnedItems ?? []).map((item) => item.name).join(', ') || scheduledItem?.name || activeSchedule?.itemId || 'Resolved for each viewer';
              return <Card key={option.id} title={option.label} intro={`${source} · ${preview}`} tone={option.id === placement ? 'info' : undefined}>
                <Button size="sm" variant="quiet" onClick={() => setPlacement(option.id)}>Manage {option.label}</Button>
              </Card>;
            })}
          </Grid>
          <div className="tabs" role="tablist" aria-label="Hero placement">
            {PLACEMENTS.map((option) => (
              <Button key={option.id} variant={placement === option.id ? 'primary' : 'quiet'} onClick={() => setPlacement(option.id)}>
                {option.label}
              </Button>
            ))}
          </div>
          <Card
            title={`${PLACEMENTS.find((entry) => entry.id === placement)?.label} hero`}
            intro={`Pinned ${PLACEMENTS.find((entry) => entry.id === placement)?.type} lead this section only. Empty places use this placement’s automatic selection.`}
            icon="star"
            tone="note"
            footer={
              <>
                <Button variant="primary" busy={busy === 'save'} onClick={() => void save()}>
                  Save hero
                </Button>
                <Button
                  onClick={() => {
                    setCurrent({ pinnedItems: [] });
                  }}
                >
                  Clear pins
                </Button>
                {dirty ? <span className="hint">Unsaved changes.</span> : null}
              </>
            }
          >
            {pins.length === 0 ? (
              <Empty>No titles are pinned. The hero is entirely release-aware and automatic.</Empty>
            ) : (
              <div className="hero-pins">
                {pins.map((item, index) => (
                  <div className="hero-pin" key={item.id}>
                    <span className="hero-pin-order">{index + 1}</span>
                    <span><b>{item.name}</b><small>{item.type}{item.year ? ` · ${item.year}` : ''}</small></span>
                    <Button size="sm" icon="clock" onClick={() => setScheduleDraft(newSchedule(item, placement))}>
                      Schedule
                    </Button>
                    <Button
                      variant="quiet"
                      size="sm"
                      icon="close"
                      title={`Remove ${item.name}`}
                      onClick={() => setCurrent({ pinnedItems: pins.filter((pin) => pin.id !== item.id) })}
                    />
                  </div>
                ))}
              </div>
            )}

            <Field
              label="Prime-card subtitle"
              hint="Optional wording under the large first card. Leave blank to use Memby's natural release or rating reason."
            >
              <input
                type="text"
                maxLength={160}
                value={current.primeSubtitle}
                placeholder="Leave blank for the automatic reason"
                onChange={(event) => {
                  setCurrent({ primeSubtitle: event.target.value });
                }}
              />
            </Field>
          </Card>

          <Card
            title="Hero schedule"
            intro="The gateway applies these rules in server time. Manual pins win first; otherwise the highest-priority active schedule wins, followed by Memby’s automatic hero."
            icon="clock"
            tone="info"
            actions={<Tag tone="info">{policy?.timeZone || 'server local time'}</Tag>}
            footer={
              <>
                <Button variant="primary" busy={busy === 'save'} onClick={() => void save()}>
                  Save schedule
                </Button>
                <span className="hint">Daily and weekly rules repeat until you switch them off.</span>
              </>
            }
          >
            {schedules.length === 0 ? (
              <Empty>No scheduled heroes yet. Use Schedule beside a pinned or searched title.</Empty>
            ) : (
              <div className="hero-schedule-list">
                {[...schedules]
                  .sort((left, right) => Number(right.enabled) - Number(left.enabled) || right.priority - left.priority)
                  .map((schedule) => {
                    const item = [...(policy?.items ?? []), ...pins, ...(results ?? [])]
                      .find((candidate) => candidate.id === schedule.itemId);
                    return (
                      <article className="hero-schedule" data-enabled={schedule.enabled || undefined} key={schedule.id}>
                        <div className="hero-schedule-time">
                          <b>{schedule.frequency === 'weekly' ? 'Weekly' : schedule.frequency === 'daily' ? 'Daily' : 'Once'}</b>
                          <span>{schedule.frequency ? schedule.startTime : schedule.startAt ? new Date(schedule.startAt).toLocaleDateString() : '—'}</span>
                        </div>
                        <div className="hero-schedule-main">
                          <div className="hero-schedule-title">
                            <h3>{item?.name ?? schedule.itemId}</h3>
                            <Tag tone={schedule.enabled ? 'ok' : undefined}>{schedule.enabled ? 'enabled' : 'paused'}</Tag>
                          </div>
                          <p>{scheduleSummary(schedule)}</p>
                          <div className="chips">
                            {(schedule.placements ?? ['home']).map((value) => (
                              <span className="chip" key={value}>{PLACEMENTS.find((entry) => entry.id === value)?.label}</span>
                            ))}
                            {schedule.priority !== 0 ? <span className="chip">Priority {schedule.priority}</span> : null}
                          </div>
                        </div>
                        <div className="hero-schedule-actions">
                          <Button size="sm" onClick={() => setScheduleDraft({ ...schedule })}>Edit</Button>
                          <Button
                            size="sm"
                            variant="quiet"
                            onClick={() => {
                              setSchedules((current) => current.map((entry) => entry.id === schedule.id ? { ...entry, enabled: !entry.enabled } : entry));
                              setDirty(true);
                            }}
                          >
                            {schedule.enabled ? 'Pause' : 'Enable'}
                          </Button>
                          <Button
                            size="sm"
                            variant="quiet"
                            onClick={() => {
                              setSchedules((current) => current.filter((entry) => entry.id !== schedule.id));
                              setDirty(true);
                            }}
                          >
                            Remove
                          </Button>
                        </div>
                      </article>
                    );
                  })}
              </div>
            )}
          </Card>

          <Card
            title="Find a title"
            intro={`Search the imported Emby catalogue. Add a result to the selected ${PLACEMENTS.find((entry) => entry.id === placement)?.label} placement; switch tabs to show it in more than one section.`}
            icon="search"
            tone="info"
          >
            <div className="field-row">
              <Field label="Title" grow>
                <input
                  type="search"
                  value={queryText}
                  placeholder="Search films and television shows"
                  onChange={(event) => setQueryText(event.target.value)}
                  onKeyDown={(event) => {
                    if (event.key === 'Enter') void search();
                  }}
                />
              </Field>
              <Button busy={busy === 'search'} icon="search" onClick={() => void search()}>
                Search
              </Button>
            </div>

            {results === null ? null : results.length === 0 ? (
              <Empty>No playable films or series matched that search.</Empty>
            ) : (
              <Grid>
                {results.map((item) => (
                  <Card key={item.id} title={item.name} intro={`${item.type || 'Title'} · ${item.year || 'Year unknown'}`}>
                    <div className="row">
                      <Button
                        size="sm"
                        icon="plus"
                        disabled={pins.some((pin) => pin.id === item.id) || (placement === 'movies' && item.type !== 'Movie') || (placement === 'tv_shows' && item.type !== 'Series')}
                        onClick={() => add(item)}
                      >
                        {pins.some((pin) => pin.id === item.id) ? 'Pinned' : 'Add to hero'}
                      </Button>
                      <Button size="sm" icon="clock" onClick={() => setScheduleDraft(newSchedule(item, placement))}>
                        Schedule
                      </Button>
                    </div>
                  </Card>
                ))}
              </Grid>
            )}
          </Card>
        </>
      )}

      {scheduleDraft ? (
        <ScheduleEditor
          schedule={scheduleDraft}
          item={[...(policy?.items ?? []), ...pins, ...(results ?? [])].find((candidate) => candidate.id === scheduleDraft.itemId)}
          timeZone={policy?.timeZone || 'server local time'}
          isNew={!schedules.some((entry) => entry.id === scheduleDraft.id)}
          onCancel={() => setScheduleDraft(null)}
          onSave={(next) => {
            setSchedules((current) => current.some((entry) => entry.id === next.id)
              ? current.map((entry) => entry.id === next.id ? next : entry)
              : [...current, next]);
            setScheduleDraft(null);
            setDirty(true);
          }}
        />
      ) : null}
    </>
  );
}

function ScheduleEditor({
  schedule,
  item,
  timeZone,
  isNew,
  onSave,
  onCancel,
}: {
  schedule: HeroSchedule;
  item?: HeroItem;
  timeZone: string;
  isNew: boolean;
  onSave: (schedule: HeroSchedule) => void;
  onCancel: () => void;
}) {
  const [draft, setDraft] = useState<HeroSchedule>({ ...schedule, weekdays: [...(schedule.weekdays ?? [])] });
  const frequency = scheduleFrequency(draft);
  const setFrequency = (next: ScheduleFrequency) => {
    const now = new Date();
    const later = new Date(now.getTime() + 2 * 60 * 60 * 1000);
    setDraft((current) => next === 'once'
      ? {
          ...current,
          frequency: undefined,
          startAt: usableAbsoluteTime(current.startAt) ? current.startAt : now.toISOString(),
          endAt: usableAbsoluteTime(current.endAt) ? current.endAt : later.toISOString(),
        }
      : {
          ...current,
          frequency: next,
          startTime: current.startTime || '18:00',
          endTime: current.endTime || '22:00',
          weekdays: next === 'weekly' ? (current.weekdays?.length ? current.weekdays : [1, 2, 3, 4, 5]) : [],
        });
  };
  const selectedPlacements = draft.placements ?? ['home'];
  const validPlacement = (value: HeroPlacement) => value === 'home' ||
    (value === 'movies' && item?.type === 'Movie') ||
    (value === 'tv_shows' && item?.type === 'Series');
  const onceValid = Boolean(draft.startAt && draft.endAt && new Date(draft.endAt) > new Date(draft.startAt));
  const recurringValid = Boolean(draft.startTime && draft.endTime && draft.startTime !== draft.endTime &&
    (frequency !== 'weekly' || (draft.weekdays ?? []).length > 0));
  const valid = frequency === 'once' ? onceValid : recurringValid;

  return (
    <div className="scrim" onPointerDown={(event) => event.target === event.currentTarget && onCancel()}>
      <div className="dialog hero-schedule-dialog" role="dialog" aria-modal="true" aria-labelledby="hero-schedule-title">
        <div className="hero-schedule-dialog-head">
          <span className="hero-schedule-kicker">Hero schedule</span>
          <h2 id="hero-schedule-title">{item?.name ?? draft.itemId}</h2>
          <p>Choose exactly when this title can lead the selected sections. Times use {timeZone}.</p>
        </div>

        <div className="schedule-frequency" role="group" aria-label="Schedule frequency">
          {(['once', 'daily', 'weekly'] as ScheduleFrequency[]).map((value) => (
            <button key={value} type="button" aria-pressed={frequency === value} onClick={() => setFrequency(value)}>
              <b>{value === 'once' ? 'One time' : value === 'daily' ? 'Every day' : 'Weekly'}</b>
              <span>{value === 'once' ? 'A date range' : value === 'daily' ? 'Same time daily' : 'Choose days'}</span>
            </button>
          ))}
        </div>

        {frequency === 'once' ? (
          <div className="fields">
            <Field label="Starts">
              <input
                type="datetime-local"
                value={draft.startAt ? localDateTime(new Date(draft.startAt)) : ''}
                onChange={(event) => setDraft((current) => ({ ...current, startAt: event.target.value ? new Date(event.target.value).toISOString() : undefined }))}
              />
            </Field>
            <Field label="Ends">
              <input
                type="datetime-local"
                value={draft.endAt ? localDateTime(new Date(draft.endAt)) : ''}
                onChange={(event) => setDraft((current) => ({ ...current, endAt: event.target.value ? new Date(event.target.value).toISOString() : undefined }))}
              />
            </Field>
          </div>
        ) : (
          <>
            <div className="fields">
              <Field label="Starts each time">
                <input type="time" value={draft.startTime ?? ''} onChange={(event) => setDraft((current) => ({ ...current, startTime: event.target.value }))} />
              </Field>
              <Field label="Ends each time" hint="An earlier end time continues into the following day.">
                <input type="time" value={draft.endTime ?? ''} onChange={(event) => setDraft((current) => ({ ...current, endTime: event.target.value }))} />
              </Field>
            </div>
            {frequency === 'weekly' ? (
              <div className="schedule-days">
                <div className="schedule-days-head">
                  <b>Days</b>
                  <div>
                    <button type="button" onClick={() => setDraft((current) => ({ ...current, weekdays: [1, 2, 3, 4, 5] }))}>Weekdays</button>
                    <button type="button" onClick={() => setDraft((current) => ({ ...current, weekdays: [6, 0] }))}>Weekend</button>
                    <button type="button" onClick={() => setDraft((current) => ({ ...current, weekdays: WEEKDAYS.map((day) => day.value) }))}>Every day</button>
                  </div>
                </div>
                <div className="schedule-day-grid">
                  {WEEKDAYS.map((day) => {
                    const selected = (draft.weekdays ?? []).includes(day.value);
                    return (
                      <button
                        type="button"
                        key={day.value}
                        aria-pressed={selected}
                        title={day.label}
                        onClick={() => setDraft((current) => ({
                          ...current,
                          weekdays: selected
                            ? (current.weekdays ?? []).filter((value) => value !== day.value)
                            : [...(current.weekdays ?? []), day.value],
                        }))}
                      >
                        {day.short}
                      </button>
                    );
                  })}
                </div>
              </div>
            ) : null}
          </>
        )}

        <div className="schedule-options">
          <div>
            <span className="schedule-option-label">Show in</span>
            <div className="schedule-placement-grid">
              {PLACEMENTS.map((option) => (
                <Toggle
                  key={option.id}
                  label={option.label}
                  checked={selectedPlacements.includes(option.id)}
                  disabled={!validPlacement(option.id)}
                  onChange={(enabled) => setDraft((current) => {
                    const selected = current.placements ?? ['home'];
                    const next = enabled ? [...selected, option.id] : selected.filter((value) => value !== option.id);
                    return { ...current, placements: next.length ? [...new Set(next)] : selected };
                  })}
                />
              ))}
            </div>
          </div>
          <Field label="Priority" hint="Higher rules win when schedules overlap.">
            <input
              type="number"
              min={-1000}
              max={1000}
              step={10}
              value={draft.priority}
              onChange={(event) => setDraft((current) => ({ ...current, priority: Number(event.target.value) }))}
            />
          </Field>
        </div>

        <Toggle
          label="Schedule enabled"
          hint="Pause it without losing its days and times."
          checked={draft.enabled}
          onChange={(enabled) => setDraft((current) => ({ ...current, enabled }))}
        />

        <div className="dialog-actions">
          <Button variant="quiet" onClick={onCancel}>Cancel</Button>
          <Button variant="primary" disabled={!valid} onClick={() => onSave(draft)}>
            {isNew ? 'Add rule' : 'Save rule'}
          </Button>
        </div>
      </div>
    </div>
  );
}
