import { GridTable } from '@theorchard/suite-components';
import { GlyphIcon } from '@theorchard/suite-icons';
import cx from 'classnames';
import dayjs from 'dayjs';
import localizedFormat from 'dayjs/plugin/localizedFormat';
import timezone from 'dayjs/plugin/timezone';
import utc from 'dayjs/plugin/utc';
import { groupBy, uniq } from 'lodash';
import React, { useState } from 'react';
import type { Dayjs } from 'dayjs';
import type { FC, ReactNode } from 'react';
import type { ReleaseSchedule, Timezone } from 'src/types';

dayjs.extend(utc);
dayjs.extend(timezone);
dayjs.extend(localizedFormat);

export const EmptyStateIcon: FC = () => (
    <div data-testid="emtpy-table-icon" className="EmptyStateIcon">
        <div className="EmptyStateIcon-outer-circle">
            <div className="EmptyStateIcon-inner-circle">
                <GlyphIcon name="calendar" size={16} />
            </div>
        </div>
    </div>
);

export interface ReleaseScheduleTableProps {
    name?: string;
    className?: string;
    testId?: string;
    releaseSchedule: ReleaseSchedule[];
    onRemove?: (schedules: ReleaseSchedule[] | ReleaseSchedule) => void;
    manageButton?: ReactNode;
    editable: boolean;
    totalServicesPrompt?: string;
}

const DISPLAY_TIMEZONES: Timezone[] = [
    { iana: 'America/Los_Angeles', offset: -5, abbreviation: 'PT' },
    { iana: 'America/New_York', offset: -8, abbreviation: 'ET' },
    { iana: 'Etc/Gmt', offset: 0, abbreviation: 'GMT' },
    { iana: 'Australia/Sydney', offset: 10, abbreviation: 'AET' },
];

const SEPARATOR = '|';

/**
 * Given a UTC datetime string of the format 'YYYY-MM-DDThh:mmZ' or a Date,
 * convert its timezone to each of those provided in an array of objects,
 * paired with the input timezone.
 */
const localize = ({
    dateTime,
    timezones,
}: {
    dateTime: string | Date;
    timezones: Timezone[];
}): { dateTime: Dayjs; timezone: Timezone }[] =>
    timezones.reduce(
        (values, timezone) => [
            ...values,
            { dateTime: dayjs.utc(dateTime).tz(timezone.iana), timezone },
        ],
        [] as { dateTime: Dayjs; timezone: Timezone }[]
    );

export const ReleaseScheduleTable: FC<ReleaseScheduleTableProps> = ({
    name,
    className,
    testId,
    releaseSchedule,
    onRemove = () => {},
    editable,
    manageButton,
    totalServicesPrompt,
}) => {
    const [selectedRows, setSelectedRows] = useState<string[]>([]);
    const isEmpty = releaseSchedule.length === 0;
    const renderTableHeader = (!editable && !isEmpty) || (editable && !isEmpty);
    const [page, setPage] = useState<number>(0);
    const [pageSize, setPageSize] = useState<number>(10);

    const countPrompt =
        totalServicesPrompt ||
        $t('releaseScheduler.releaseScheduleTable.scheduledReleases');

    const data = releaseSchedule.slice(
        pageSize * page,
        pageSize * page + pageSize
    );

    return (
        <div
            data-testid={testId}
            className={cx('ReleaseScheduleTable', className)}
        >
            <h3>{name}</h3>
            <GridTable
                bordered={!isEmpty}
                shadowed={!isEmpty}
                data={data}
                rowKey="deliveryStore.id"
                selectable={editable}
                selectedRowKeys={selectedRows}
                onSelectedRowsChanged={setSelectedRows}
                emptyStateIcon={() => <EmptyStateIcon />}
                paginated={!isEmpty}
                page={page}
                pageSize={pageSize}
                paginationText={renderTableHeader ? countPrompt : undefined}
                onPageChange={setPage}
                onPageSizeChange={setPageSize}
                totalCount={releaseSchedule.length}
                emptyStateTitle={
                    manageButton ||
                    $t('releaseScheduler.releaseScheduleTable.emptySchedule')
                }
                rowBulkActions={{
                    delete: () => {
                        const filteredReleases = releaseSchedule.filter(
                            release =>
                                selectedRows.some(
                                    deliveryStore =>
                                        deliveryStore ===
                                        release.deliveryStore.id
                                )
                        );
                        onRemove(filteredReleases);
                        setSelectedRows([]);
                    },
                }}
                rowActions={
                    editable
                        ? {
                              delete: row => {
                                  onRemove(row);
                                  setSelectedRows(
                                      selectedRows.filter(
                                          selectedId =>
                                              selectedId !==
                                              row.deliveryStore.id
                                      )
                                  );
                              },
                          }
                        : undefined
                }
            >
                <GridTable.Column
                    title={$t(
                        'releaseScheduler.releaseScheduleTable.columnHeaders.store'
                    )}
                    name="deliveryStore.name"
                />
                <GridTable.Column
                    title={$t(
                        'releaseScheduler.releaseScheduleTable.columnHeaders.saleDate'
                    )}
                    minWidth="max-content"
                    maxWidth="200px"
                    className="col-saleDate"
                    Cell={({ row: { data } }) => {
                        const { saleDate, saleDateTime } = data as {
                            saleDate?: string;
                            saleDateTime?: string;
                        };
                        return (
                            <div>
                                {saleDateTime
                                    ? uniq(
                                          localize({
                                              dateTime: saleDateTime,
                                              timezones: DISPLAY_TIMEZONES,
                                          }).map(({ dateTime }) =>
                                              dateTime.format('L')
                                          )
                                      ).reduce(
                                          (
                                              acc:
                                                  | (string | JSX.Element)[]
                                                  | null,
                                              x: string,
                                              i: number
                                          ) =>
                                              acc === null
                                                  ? [x]
                                                  : [
                                                        ...acc,
                                                        <span
                                                            key={i}
                                                            className="datetime-seperator"
                                                        >
                                                            {' '}
                                                            {SEPARATOR}{' '}
                                                        </span>,
                                                        x,
                                                    ],
                                          null
                                      )
                                    : dayjs(saleDate).format('L')}
                            </div>
                        );
                    }}
                />
                <GridTable.Column
                    minWidth="max-content"
                    title={$t(
                        'releaseScheduler.releaseScheduleTable.columnHeaders.saleTime'
                    )}
                    className="col-saleTime"
                    Cell={({ row: { data } }) => {
                        const { saleDateTime } = data as {
                            saleDateTime?: string;
                        };
                        return (
                            <div>
                                {saleDateTime
                                    ? Object.values(
                                          groupBy(
                                              localize({
                                                  dateTime: saleDateTime,
                                                  timezones: DISPLAY_TIMEZONES,
                                              }).map(
                                                  ({
                                                      dateTime,
                                                      timezone: {
                                                          abbreviation,
                                                      },
                                                  }) => ({
                                                      date: dateTime.format(
                                                          'L'
                                                      ),
                                                      time: `${dateTime.format(
                                                          'LT'
                                                      )} ${abbreviation}`,
                                                  })
                                              ),
                                              'date'
                                          )
                                      )
                                          .map(times =>
                                              times
                                                  .map(({ time }) => time)
                                                  .join(', ')
                                          )
                                          .reduce(
                                              (
                                                  acc:
                                                      | (string | JSX.Element)[]
                                                      | null,
                                                  x: string,
                                                  i: number
                                              ) =>
                                                  acc === null
                                                      ? [x]
                                                      : [
                                                            ...acc,
                                                            <span
                                                                key={i}
                                                                className="datetime-seperator"
                                                            >
                                                                {' '}
                                                                {SEPARATOR}{' '}
                                                            </span>,
                                                            x,
                                                        ],
                                              null
                                          )
                                    : $t(
                                          'releaseScheduler.releaseScheduleTable.staggeredSaleTime'
                                      )}
                            </div>
                        );
                    }}
                />
            </GridTable>
        </div>
    );
};

export default ReleaseScheduleTable;
