import { OneDayMillis } from '@/helpers/constants.ts';
import { OneWeekMillis, getTimeSlotId } from '@/helpers/slotSchedulerUtil.ts';
import { useSlotProgramOptions } from '@/hooks/programming_controls/useSlotProgramOptions';
import { useScheduledSlotProgramDetails } from '@/hooks/slot_scheduler/useScheduledSlotProgramDetails.ts';
import { Delete, Edit, Warning } from '@mui/icons-material';
import {
  Box,
  Dialog,
  DialogTitle,
  IconButton,
  Stack,
  Tab,
  Tabs,
  Tooltip,
} from '@mui/material';
import type { TimeSlot, TimeSlotProgramming } from '@tunarr/types/api';
import dayjs from 'dayjs';
import localizedFormat from 'dayjs/plugin/localizedFormat';
import {
  capitalize,
  filter,
  find,
  isEmpty,
  isNil,
  map,
  nth,
  range,
  sortBy,
  uniq,
} from 'lodash-es';
import type {
  MRT_ColumnDef,
  MRT_Row,
  MRT_TableInstance,
} from 'material-react-table';
import {
  MaterialReactTable,
  useMaterialReactTable,
} from 'material-react-table';
import pluralize from 'pluralize';
import { useMemo, useState } from 'react';
import { useDayjs } from '../../hooks/useDayjs.ts';
import { useTimeSlotFormContext } from '../../hooks/useTimeSlotFormContext.ts';
import { AddTimeSlotButton } from './AddTimeSlotButton.tsx';
import { ClearSlotsButton } from './ClearSlotsButton.tsx';
import { EditTimeSlotDialogContent } from './EditTimeSlotDialogContent.tsx';
import type { SlotWarning, TimeSlotTableRowType } from './SlotTypes.ts';
import { TimeSlotWarningsDialog } from './TimeSlotWarningsDialog.tsx';

dayjs.extend(localizedFormat);

export const TimeSlotTable = () => {
  const providedDjs = useDayjs();
  const localeData = useMemo(() => providedDjs().localeData(), [providedDjs]);
  const { watch, slotArray } = useTimeSlotFormContext();
  const [currentPeriod, latenessMs] = watch(['period', 'latenessMs']);
  const { dropdownOpts: programOptions } = useSlotProgramOptions();
  const startOfPeriod = dayjs().startOf(currentPeriod);
  const slotIds = useMemo(
    () =>
      uniq(map(slotArray.fields, (slot) => getTimeSlotId(slot.programming))),
    [slotArray.fields],
  );
  const [selectedDay, setSelectedDay] = useState(0);

  const [pagination, setPagination] = useState({
    pageIndex: 0,
    pageSize: 25,
  });

  const detailsBySlotId = useScheduledSlotProgramDetails(slotIds);

  const [currentEditingSlot, setCurrentEditingSlot] = useState<{
    slot: TimeSlot;
    index: number;
  } | null>(null);

  const [currentSlotWarningsIndex, setCurrentSlotWarningsIndex] = useState<
    number | null
  >(null);

  const rows = useMemo(() => {
    return map(
      sortBy(
        slotArray.fields
          .map((slot, index) => ({
            ...slot,
            originalIndex: index,
          }))
          .filter((slot) => {
            if (currentPeriod !== 'week') {
              return true;
            }
            const start = OneDayMillis * selectedDay;
            const end = start + OneDayMillis;
            return slot.startTime >= start && slot.startTime < end;
          }),
        (slot) => slot.startTime,
      ),
      (slot, i, slots) => {
        const next = slots[(i + 1) % slots.length];
        const scale =
          i === slots.length - 1
            ? currentPeriod === 'week'
              ? OneWeekMillis
              : OneDayMillis
            : 0;
        const slotDuration = next.startTime + scale - slot.startTime;
        const warnings: SlotWarning[] = [];
        const slotId = getTimeSlotId(slot.programming);
        const slotDetails = detailsBySlotId[slotId];
        let programCount = 0;
        if (slotDetails) {
          const overDuration = filter(
            slotDetails.programDurations,
            ({ duration }) => duration > slotDuration + latenessMs,
          );

          if (overDuration.length > 0) {
            warnings.push({
              type: 'program_too_long',
              programs: overDuration,
            });
          }

          programCount = slotDetails.programCount;
        }

        return {
          ...slot,
          durationMs: slotDuration,
          warnings,
          programCount,
        } satisfies TimeSlotTableRowType;
      },
    );
  }, [
    currentPeriod,
    detailsBySlotId,
    latenessMs,
    selectedDay,
    slotArray.fields,
  ]);

  const columns = useMemo<MRT_ColumnDef<TimeSlotTableRowType>[]>(() => {
    return [
      {
        header: '',
        muiTableBodyCellProps: () => ({
          sx: {
            textAlign: 'center',
          },
        }),
        id: 'status',
        Cell: ({ row }) => {
          if (!isEmpty(row.original.warnings)) {
            const len = row.original.warnings.length;
            return (
              <Tooltip
                title={`There ${pluralize('is', len)} ${len} ${pluralize(
                  'warning',
                  len,
                )}. Click for details.`}
              >
                <IconButton
                  onClick={() => setCurrentSlotWarningsIndex(row.index)}
                  size="small"
                  sx={{ fontSize: '1rem', py: 0 }}
                  disableRipple
                >
                  <Warning sx={{ fontSize: 'inherit' }} color="warning" />
                </IconButton>
              </Tooltip>
            );
          }
          return null;
        },
        size: 40,
        enableHiding: false,
        enableColumnActions: false,
      },
      {
        header: 'Start Time',
        accessorKey: 'startTime',
        Cell: ({ cell }) => {
          const value = cell.getValue<number>();
          const dateTime = startOfPeriod.add(value);
          return currentPeriod === 'day'
            ? dateTime.format('LT')
            : dateTime.format('dddd LT');
        },
        size: 100,
        grow: false,
      },
      {
        header: 'Program',
        accessorKey: 'programming',
        enableEditing: true,
        Cell: ({ cell }) => {
          const value = cell.getValue<TimeSlotProgramming>();
          switch (value.type) {
            case 'movie':
              return 'Movie';
            case 'show':
              return find(programOptions, { showId: value.showId })
                ?.description;
            case 'flex':
              return 'Flex';
            case 'redirect':
              return find(programOptions, { channelId: value.channelId })
                ?.description;
            case 'custom-show': {
              const showName = find(programOptions, {
                customShowId: value.customShowId,
              })?.description;
              return `Custom Show - ${showName}`;
            }
          }
        },
        grow: true,
        size: 350,
      },
      {
        header: '# of Programs',
        id: 'programCount',
        enableEditing: false,
        Cell({ row }) {
          const programming = row.original.programming;
          switch (programming.type) {
            case 'movie':
            case 'show':
            case 'custom-show':
              return row.original.programCount;
            case 'flex':
            case 'redirect':
              return '-';
          }
        },
      },
      {
        header: 'Order',
        accessorFn(originalRow) {
          switch (originalRow.programming.type) {
            case 'flex':
            case 'redirect':
              return null;
            case 'movie':
            case 'show':
            case 'custom-show':
              return capitalize(originalRow.order);
          }
        },
        id: 'programOrder',
        Cell({ cell }) {
          const value = cell.getValue<TimeSlot['order'] | null>();
          if (!value) {
            return '-';
          }
          return value;
        },
        enableSorting: false,
      },
    ];
  }, [currentPeriod, programOptions, startOfPeriod]);

  const renderActionCell = ({
    row,
  }: {
    row: MRT_Row<TimeSlotTableRowType>;
    table: MRT_TableInstance<TimeSlotTableRowType>;
  }) => {
    return (
      <>
        <Tooltip title="Edit Slot" placement="top">
          <IconButton
            onClick={() =>
              setCurrentEditingSlot({
                slot: row.original,
                index: row.original.originalIndex,
              })
            }
          >
            <Edit />
          </IconButton>
        </Tooltip>
        <Tooltip title="Delete Slot" placement="top">
          <IconButton
            onClick={() => slotArray.remove(row.original.originalIndex)}
          >
            <Delete />
          </IconButton>
        </Tooltip>
      </>
    );
  };

  const table = useMaterialReactTable({
    columns,
    data: rows,
    getRowId: (row) => row.id,
    displayColumnDefOptions: {
      'mrt-row-actions': {
        grow: false,
        Header: '',
        visibleInShowHideMenu: false,
      },
    },
    positionActionsColumn: 'last',
    enableRowActions: true,
    // TODO: Can enable this with custom options to filter by show name
    enableGlobalFilter: false,
    enableFullScreenToggle: false,
    renderRowActions: renderActionCell,
    renderTopToolbarCustomActions() {
      return (
        <Stack direction="row" alignItems="center" gap={2} useFlexGap>
          <AddTimeSlotButton
            onAdd={(slot) =>
              setCurrentEditingSlot({ slot, index: slotArray.fields.length })
            }
            programOptions={programOptions}
            dayOffset={selectedDay}
          />
          <ClearSlotsButton
            fields={slotArray.fields}
            remove={slotArray.remove}
          />
        </Stack>
      );
    },
    initialState: {
      density: 'compact',
    },
    autoResetPageIndex: false,
    onPaginationChange: setPagination,
    state: {
      pagination,
    },
  });

  return (
    <>
      {currentPeriod === 'week' && (
        <Box
          sx={{ width: '100%', borderBottom: 1, borderColor: 'divider', mb: 2 }}
        >
          <Tabs
            value={selectedDay}
            onChange={(_, v) => setSelectedDay(v as number)}
          >
            {range(0, 7).map((idx) => (
              <Tab
                key={idx}
                value={idx}
                label={<span>{localeData.weekdaysShort()[idx]}</span>}
              />
            ))}
          </Tabs>
        </Box>
      )}
      <MaterialReactTable table={table} />
      <Dialog
        maxWidth="sm"
        open={!!currentEditingSlot}
        fullWidth
        onClose={() => setCurrentEditingSlot(null)}
      >
        <DialogTitle>Edit Slot</DialogTitle>
        {currentEditingSlot && (
          <EditTimeSlotDialogContent
            slot={currentEditingSlot.slot}
            index={currentEditingSlot.index}
            programOptions={programOptions}
            onClose={() => setCurrentEditingSlot(null)}
          />
        )}
      </Dialog>
      <TimeSlotWarningsDialog
        slot={
          !isNil(currentSlotWarningsIndex)
            ? nth(rows, currentSlotWarningsIndex)
            : undefined
        }
        onClose={() => setCurrentSlotWarningsIndex(null)}
      />
    </>
  );
};
