import { useCallback, useEffect, useRef, useState } from 'react';
import { Plus } from 'lucide-react';
import { Button, Dialog, Select, Spinner } from '../../components';
import {
  addScheduleItem,
  getSchedules,
  type LibraryBrowseMediaType,
  type ProgramSchedule
} from '../../api';
import { ApiError } from '../../api/client';
import { collectionTypeForMediaType, scheduleItemRequestForMediaItem } from './scheduleItem';

export interface AddToScheduleItem {
  mediaType: LibraryBrowseMediaType;
  id: number;
  title: string;
}

export interface AddToScheduleDialogProps {
  open: boolean;
  onClose: () => void;
  item: AddToScheduleItem;
  onAdded?: (scheduleName: string) => void;
}

function messageFromError(error: unknown, fallback: string): string {
  if (error instanceof ApiError) {
    return error.detail ?? error.message;
  }

  if (error instanceof Error) {
    return error.message;
  }

  return fallback;
}

export function AddToScheduleDialog(props: AddToScheduleDialogProps) {
  if (!props.open) {
    return null;
  }

  return <AddToScheduleDialogBody {...props} />;
}

function AddToScheduleDialogBody({ onClose, item, onAdded }: AddToScheduleDialogProps) {
  const [loading, setLoading] = useState(true);
  const [loadError, setLoadError] = useState<string | null>(null);
  const [schedules, setSchedules] = useState<ProgramSchedule[]>([]);
  const [selected, setSelected] = useState<string>('');
  const [submitting, setSubmitting] = useState(false);
  const [submitError, setSubmitError] = useState<string | null>(null);
  const activeRef = useRef(true);

  useEffect(() => {
    activeRef.current = true;

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

  const load = useCallback(() => {
    getSchedules()
      .then((result) => {
        if (!activeRef.current) {
          return;
        }

        setSchedules(result);
        setSelected(result.length > 0 ? String(result[0].id) : '');
        setLoadError(null);
        setLoading(false);
      })
      .catch((error: unknown) => {
        if (!activeRef.current) {
          return;
        }

        setLoadError(messageFromError(error, 'Unable to load schedules'));
        setLoading(false);
      });
  }, []);

  useEffect(() => {
    load();
  }, [load]);

  const collectionType = collectionTypeForMediaType(item.mediaType);
  const canSubmit = !submitting && selected !== '' && collectionType !== null;

  const submit = () => {
    if (!canSubmit || collectionType === null) {
      return;
    }

    setSubmitting(true);
    setSubmitError(null);

    const scheduleId = Number(selected);
    const name = schedules.find((schedule) => String(schedule.id) === selected)?.name ?? 'schedule';
    const payload = scheduleItemRequestForMediaItem(collectionType, item.id);

    addScheduleItem(scheduleId, payload)
      .then(() => {
        if (!activeRef.current) {
          return;
        }

        onAdded?.(name);
        onClose();
      })
      .catch((error: unknown) => {
        if (!activeRef.current) {
          return;
        }

        setSubmitError(messageFromError(error, 'Unable to add to schedule'));
        setSubmitting(false);
      });
  };

  return (
    <Dialog
      footer={
        <>
          <Button disabled={submitting} onClick={onClose} variant="secondary">
            Cancel
          </Button>
          <Button
            disabled={!canSubmit}
            loading={submitting}
            onClick={submit}
            startIcon={<Plus aria-hidden="true" size={14} />}
            variant="primary"
          >
            Add to schedule
          </Button>
        </>
      }
      onClose={onClose}
      open
      title={`Add “${item.title}” to schedule`}
      width={460}
    >
      {loading ? (
        <div style={{ display: 'flex', justifyContent: 'center', padding: 24 }}>
          <Spinner />
        </div>
      ) : loadError ? (
        <span className="ctv-field-error" role="alert">
          {loadError}
        </span>
      ) : collectionType === null ? (
        <p className="ctv-collections-picker-note">This item kind can&rsquo;t be added to a schedule.</p>
      ) : schedules.length === 0 ? (
        <p className="ctv-collections-picker-note">No schedules exist yet. Create one first.</p>
      ) : (
        <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
          <Select
            label="Schedule"
            onChange={(event) => setSelected(event.target.value)}
            options={schedules.map((schedule) => ({ value: String(schedule.id), label: schedule.name ?? `Schedule ${schedule.id}` }))}
            value={selected}
          />
          {submitError && (
            <span className="ctv-field-error" role="alert">
              {submitError}
            </span>
          )}
        </div>
      )}
    </Dialog>
  );
}
