import { useCallback, useState } from 'react';

import { Box, Button, Flex, Input, Table, Text } from '@chakra-ui/react';
import { LuTrash } from 'react-icons/lu';
import { MdCloudUpload } from 'react-icons/md';

import {
  Media,
  MediaResource,
  RestErrorResponse,
  Season,
  Series,
  SeriesResource,
  Type,
  TypeResource
} from '@/back-api';
import { PageLayout } from '@/components/Layout/PageLayout';
import { TopBar } from '@/components/TopBar/TopBar';
import { FormSelect } from '@/components/form/FormSelect';
import { Formidable, useFormidable } from '@/components/formidable';
import { PopUpUploadProgress } from '@/components/popup/PopUpUploadProgress';
import {
  NumberInputField,
  NumberInputRoot,
} from '@/components/ui/number-input';
import { useMediaService } from '@/service/Media';
import { useOrderedSeries, useOrderedSeriesWithType, useSeriesService } from '@/service/Series';
import { useServiceContext } from '@/service/ServiceContext';
import { useOrderedTypes, useTypeService } from '@/service/Type';
import { isNullOrUndefined } from '@/utils/validator';

export class ElementList {
  constructor(
    public id?: number,
    public label?: string
  ) {
    // nothing to do.
  }
}
export class FileParsedElement {
  public isSended: boolean = false;
  public nameDetected: boolean = false;
  public mediaIdDetected: boolean = false;
  public seasonId?: Season['id'] = undefined;
  public seriesId?: Series['id'] = undefined;
  constructor(
    public uniqueId: number,
    public file: File,
    public title: string,
    public universe?: string,
    public series?: string,
    public season?: number,
    public mediaId?: number
  ) {
    console.log(`Unique element: ${uniqueId}`);
    // nothing to do.
  }
}
export class FileFailParsedElement {
  constructor(
    public uniqueId: number,
    public file: File,
    public reason: string
  ) {
    console.log(`Unique element2: ${uniqueId}`);
    // nothing to do.
  }
}

type FormInsertData = {
  typeId?: number;
  seriesId?: number;
  titleSeason?: string;
};

export const AddPage = () => {
  const [parsedElement, setParsedElement] = useState<FileParsedElement[]>([]);
  const [parsedFailedElement, setParsedFailedElement] = useState<
    FileFailParsedElement[] | undefined
  >(undefined);

  const [needSend, setNeedSend] = useState<boolean>(false);

  // list of all files already registered in the bdd to compare with the current list of files.
  const [listFileInBdd, setListFileInBdd] = useState<any[] | undefined>(
    undefined
  );

  const { dataTypes } = useOrderedTypes();
  const { dataSeries: dataSeriesFull } = useOrderedSeries();
  const { store: storeType } = useTypeService();
  const { store: storeSeries } = useSeriesService();
  const { store: storeMedia } = useMediaService();
  const { session } = useServiceContext();

  const form = useFormidable<FormInsertData>({
    configuration: {
      enableModifyNotification: false,
      enableReset: false,
    },
  });
  // I think this does not work ...
  const { dataSeries } = useOrderedSeriesWithType(form.values["typeId"]);

  const updateNeedSend = () => {
    if (parsedElement.length === 0) {
      setNeedSend(false);
      return;
    }
    let tmp = true;
    for (let iii = 0; iii < parsedElement.length; iii++) {
      if (
        isNullOrUndefined(parsedElement[iii].title) ||
        parsedElement[iii].title === ''
      ) {
        tmp = false;
      }
    }
    setNeedSend(tmp);
  };

  const onTitle = (data: FileParsedElement, value: any): void => {
    data.title = value;
    setParsedElement([...parsedElement]);
    updateNeedSend();
  };

  const removeElementFromList = (data: FileParsedElement, value: any): void => {
    const parsedElementTmp = [...parsedElement];
    for (let iii = 0; iii < parsedElementTmp.length; iii++) {
      if (parsedElementTmp[iii] === data) {
        parsedElementTmp.splice(iii, 1);
        break;
      }
    }
    setParsedElement(parsedElementTmp);
    setParsedFailedElement((previous) => [
      ...(previous ?? []),
      new FileFailParsedElement(
        previous?.length ?? 0,
        data.file,
        'Removed by user.'
      ),
    ]);
    updateNeedSend();
  };

  const onMediaId = (data: FileParsedElement, value: any): void => {
    data.mediaId = value;
    setParsedElement([...parsedElement]);
    updateNeedSend();
  };
  const onSeasonId = (data: FileParsedElement, value: any): void => {
    data.season = value;
    setParsedElement([...parsedElement]);
    updateNeedSend();
  };

  const clearData = () => {
    setParsedElement([]);
    setParsedFailedElement(undefined);
    setListFileInBdd(undefined);
    setSuggestedSeries(undefined);
    //setSuggestedSeason(undefined);
  };
  
  const regex = /^(?:(?<universe>[\w. -]+):)?((?<series>[\w. -]+?)((-s| S)(?<season>\d{1,5}))?(?:(-e|E)(?<episode>\d{1,5}))[- ])?\s*(?<title>.+?)\.(webm|WEBM|Webm|mkv|MKV|Mkv)$/;

  const addFileWithMetaData = (file: File, id: number) => {
    // parsedElement: FileParsedElement[] = [];
    let universe: string | undefined = undefined;
    let series: string | undefined = undefined;
    let season: number | undefined = undefined;
    let mediaIdNumber: number | undefined = undefined;
    let title: string = '';

    form.restoreValues();

    console.log(`select file ${file.name}`);

    const match = file.name.match(regex);
    if (match?.groups) {
        universe = match.groups.universe || undefined;
        series = match.groups.series ? match.groups.series.trim() : undefined;
        season = match.groups.season? parseInt(match.groups.season, 10) : undefined;
        mediaIdNumber = match.groups.episode ? parseInt(match.groups.episode, 10) : undefined;
        title = match.groups.title.trim();
    } else {
      console.log("❌ not match :", file.name);
      title = file.name.trim();
    }

    if (season && isNaN(season)) {
      season = undefined;
    }
    if (mediaIdNumber && isNaN(mediaIdNumber)) {
      mediaIdNumber = undefined;
    }
    // remove extension
    title = title.replace(new RegExp('\\.(webm|WEBM|Webm|mkv|MKV|Mkv)'), '');
    let tmp = new FileParsedElement(
      id,
      file,
      title,
      universe,
      series,
      season,
      mediaIdNumber
    );
    console.log(`==>${JSON.stringify(tmp, null, 2)}`);

    // add it in the list.
    return tmp;
  };
  const [suggestedSeries, setSuggestedSeries] = useState<string | undefined>(
    undefined
  );
  // const [suggestedSeason, setSuggestedSeason] = useState<string | undefined>(
  //   undefined
  // );

  const onChangeFile = (value: any): void => {
    clearData();

    const parsedElementTmp: FileParsedElement[] = [];
    const parsedFailedElementTmp: FileFailParsedElement[] = [];

    for (let iii = 0; iii < value.target.files?.length; iii++) {
      parsedElementTmp.push(addFileWithMetaData(value.target.files[iii], iii));
    }
    // check if all global parameters are generic:
    if (parsedElementTmp.length === 0) {
      updateNeedSend();
      return;
    }

    // clean different Series:
    for (let iii = 1; iii < parsedElementTmp.length; iii++) {
      console.log(
        `check Series [${iii + 1}/${parsedElementTmp.length}] '${parsedElementTmp[0].series} !== ${parsedElementTmp[iii].series}'`
      );
      if (parsedElementTmp[0].series !== parsedElementTmp[iii].series) {
        parsedFailedElementTmp.push(
          new FileFailParsedElement(
            parsedFailedElementTmp.length,
            parsedElementTmp[iii].file,
            'Remove from list due to wrong Series value'
          )
        );
        console.log(
          `Remove from list (!= Series) : [${iii + 1}/${parsedElementTmp.length}] '${parsedElementTmp[iii].file.name}'`
        );
        parsedElementTmp.splice(iii, 1);
        iii--;
      }
    }
    setParsedElement(parsedElementTmp);
    setParsedFailedElement(parsedFailedElementTmp);
    console.log(`check : ${JSON.stringify(parsedElementTmp[0])}`);

    // find seriesId:
    console.log(`try find Series : ${parsedElementTmp[0].series}`);
    let SeriesFound = false;
    dataSeriesFull.forEach((data) => {
      if (
        data.name?.toLowerCase() === parsedElementTmp[0].series?.toLowerCase()
      ) {
        console.log(`    find Series : ${data.id}`);
        form.setValues({ seriesId: data.id });
        form.setValues({ typeId: data.parentId });
        SeriesFound = true;
      }
    });
    if (!SeriesFound) {
      console.log(`    set Suggested Series : ${parsedElementTmp[0].series}`);
      setSuggestedSeries(parsedElementTmp[0].series);
    } else {
      setSuggestedSeries(undefined);
    }
    updateNeedSend();
  };

  const [indexUpload, setIndexUpload] = useState<number | undefined>(undefined);
  const [listValues, setListValues] = useState<string[]>([]);
  const [currentPosition, setCurrentPosition] = useState<number>(0);
  const [totalPosition, setTotalPosition] = useState<number>(0);
  const [uploadError, setUploadError] = useState<string | undefined>(undefined);
  const [isFinishedUpload, setIsFinishedUpload] = useState<boolean>(false);

  const progressUpload = useCallback(
    (count: number, total: number) => {
      setTotalPosition(total);
      setCurrentPosition(count);
    },
    [setTotalPosition, setCurrentPosition]
  );

  const uploadNext = useCallback(
    (index: number = 0): void => {
      // if (indexUpload == undefined) {
      //   return;
      // }
      if (parsedElement.length <= index) {
        console.log('end of upload');
        setIsFinishedUpload(true);
        return;
      }
      setIndexUpload(index);
      console.log(
        `Start upload of file:  ${index}: ${parsedElement[index].title} ==> ${JSON.stringify(parsedElement[index], null, 2)}`
      );
      const data = {
        title: parsedElement[index].title,
        file: parsedElement[index].file,
        universeId: `${form.values['universeId']}`,
        typeId: `${form.values['typeId']}`,
        seriesId: `${form.values['seriesId']}`,
        season: `${parsedElement[index].season}`,
        episode: `${parsedElement[index].mediaId}`,
      };
      console.log(`data= ${JSON.stringify(data, null, 2)}`);
      console.error("Not_ implemented");
      storeMedia
        .update(
          MediaResource.uploadMedia({
            restConfig: session.getRestConfig(),
            data,
            callbacks: {
              progressUpload: progressUpload,
            },
          })
        )
        .then((data: Media) => {
          // element sended good
          // Send next ...
          uploadNext(index + 1);
        })
        .catch((error: RestErrorResponse) => {
          // TODO: manage error
          console.log(`element error: ${JSON.stringify(error, null, 2)}`);
          setUploadError(JSON.stringify(error, null, 2));
        });
    },
    [
      setUploadError,
      setIndexUpload,
      storeMedia,
      parsedElement,
      form,
      form.values,
      indexUpload,
    ]
  );

  const sendFile = (_data): void => {
    console.log(`Send file requested ... ${parsedElement.length}`);
    setUploadError(undefined);
    setIsFinishedUpload(false);
    setListValues(parsedElement.map((element) => element.file.name));
    uploadNext();
  };

  function onUploadAbort(): void {
    setIndexUpload(undefined);
  }

  function OnUploadClose(): void {
    setIndexUpload(undefined);
  }

  const addNewType = (data: string): Promise<Type> => {
    return storeType.update(
      TypeResource.post({
        restConfig: session.getRestConfig(),
        data: {
          name: data,
        },
      })
    );
  };
  const addNewSeries = (data: string): Promise<Series> => {
    return storeSeries.update(
      SeriesResource.post({
        restConfig: session.getRestConfig(),
        data: {
          name: data,
          parentId: form.values["typeId"]
        },
      })
    );
  };

  return (
    <>
      <TopBar title="Add new media" />
      <PageLayout>
        <Flex
          direction="column"
          width="80%"
          marginX="auto"
          padding="10px"
          gap="10px"
        >
          <Flex direction="column" width="full">
            <Flex>
              <Text flex={1}>format:</Text>
              <Text flex={4}>
                The format of the media permit to automatic find meta-data:<br />
                <li>Universe:Series name-s05-e22-Title.webm/mkv</li>
                <li>Universe:Series name S05E22 Title.webm/mkv</li>
                <b>example:</b> Stargate:SG1-s05-e22-Tolans.webm
              </Text>
            </Flex>
            <Flex>
              <Text flex={1}>Media:</Text>
              <Input
                flex={4}
                type="file"
                placeholder="Select a media file"
                accept=".webm,.mkv"
                multiple
                onChange={onChangeFile}
              />
            </Flex>
          </Flex>
          {parsedElement && parsedElement.length !== 0 && (
            <Formidable.From form={form} onSubmit={sendFile}>
              <Text fontSize="30px">Meta-data:</Text>
              <FormSelect
                label="Type"
                name="typeId"
                options={dataTypes}
                addNewItem={addNewType}
                isRequired
              />
              <FormSelect
                label="Series"
                name="seriesId"
                options={dataSeries}
                addNewItem={addNewSeries}
                suggestion={suggestedSeries}
                disabled={form.values["typeId"] === undefined}
              />

              <Table.Root
                colorPalette="striped"
                colorScheme="teal"
                background="gray.700"
              >
                <Table.Header>
                  <Table.Row>
                    <Table.ColumnHeader width="10%">
                      Season ID
                    </Table.ColumnHeader>
                    <Table.ColumnHeader width="10%">
                      Media ID
                    </Table.ColumnHeader>
                    <Table.ColumnHeader width="full">Title</Table.ColumnHeader>
                    <Table.ColumnHeader>actions</Table.ColumnHeader>
                  </Table.Row>
                </Table.Header>
                <Table.Body>
                  {parsedElement.map((data) => (
                    <Table.Row key={data.uniqueId}>
                      <Table.Cell>
                        {form.values["seriesId"] &&
                        <NumberInputRoot
                          value={data.season ? `${data.season}` : undefined}
                          onValueChange={(e) => onSeasonId(data, e.value)}
                          min={0}
                          max={5000}
                          backgroundColor={
                            data.mediaIdDetected === true
                              ? 'darkred'
                              : undefined
                          }
                        >
                          <NumberInputField />
                        </NumberInputRoot>
                        }
                      </Table.Cell>
                      <Table.Cell>
                        {form.values["seriesId"] &&
                        <NumberInputRoot
                          value={data.mediaId ? `${data.mediaId}` : undefined}
                          onValueChange={(e) => onMediaId(data, e.value)}
                          min={0}
                          max={5000}
                          backgroundColor={
                            data.mediaIdDetected === true
                              ? 'darkred'
                              : undefined
                          }
                        >
                          <NumberInputField />
                        </NumberInputRoot>
                        }
                      </Table.Cell>
                      <Table.Cell>
                        <Input
                          type="text"
                          placeholder="Name of the Media"
                          value={data.title}
                          onChange={(e) => onTitle(data, e.target.value)}
                          backgroundColor={
                            data.title === '' ? 'darkred' : undefined
                          }
                        />
                        {data.nameDetected === true && (
                          <>
                            <br />
                            <Text as="span" color="@danger">
                              ^^^This title already exist !!!
                            </Text>
                          </>
                        )}
                      </Table.Cell>
                      <Table.Cell>
                        <Button
                          colorPalette="@danger"
                          onClick={(e) => removeElementFromList(data, e.target)}
                        >
                          <LuTrash /> Remove
                        </Button>
                      </Table.Cell>
                    </Table.Row>
                  ))}
                </Table.Body>
              </Table.Root>
              <Flex marginY="15px">
                <Button
                  colorPalette="brand"
                  type="submit"
                  disabled={!needSend}
                  marginLeft="auto"
                  marginRight="30px"
                >
                  <MdCloudUpload /> Upload
                </Button>
              </Flex>
            </Formidable.From>
          )}

          {listFileInBdd && (
            <Table.Root
              fontPalette="striped"
              colorScheme="teal"
              background="gray.700"
            >
              <Table.Header>
                <Table.Row>
                  <Table.ColumnHeader>Media ID</Table.ColumnHeader>
                  <Table.ColumnHeader width="full">Title</Table.ColumnHeader>
                  <Table.ColumnHeader>actions</Table.ColumnHeader>
                </Table.Row>
              </Table.Header>
              <Table.Body>
                {listFileInBdd.map((data) => (
                  <Table.Row>
                    <Table.Cell>
                      <Text
                        color={
                          data.episodeDetected === true ? 'red' : undefined
                        }
                      >
                        {data.MediaId}
                      </Text>
                    </Table.Cell>
                    <Table.Cell>
                      <Text
                        color={data.nameDetected === true ? 'red' : undefined}
                      >
                        {data.title}
                      </Text>
                    </Table.Cell>
                    <Table.Cell></Table.Cell>
                  </Table.Row>
                ))}
              </Table.Body>
            </Table.Root>
          )}

          {parsedFailedElement && (
            <>
              <Text fontSize="30px">Rejected:</Text>
              <Table.Root
                colorPalette="striped"
                colorScheme="teal"
                background="gray.700"
              >
                <Table.Header>
                  <Table.Row>
                    <Table.ColumnHeader maxWidth="80%">file</Table.ColumnHeader>
                    <Table.ColumnHeader>Reason</Table.ColumnHeader>
                  </Table.Row>
                </Table.Header>
                <Table.Body>
                  {parsedFailedElement.map((data) => (
                    <Table.Row key={data.uniqueId}>
                      <Table.Cell>{data.file.name}</Table.Cell>
                      <Table.Cell>{data.reason}</Table.Cell>
                    </Table.Row>
                  ))}
                </Table.Body>
              </Table.Root>
            </>
          )}
          <Box height="250px" width="full" />
        </Flex>
        {indexUpload !== undefined && (
          <PopUpUploadProgress
            title="Upload File(s)"
            currentSize={currentPosition}
            totalSize={totalPosition}
            index={indexUpload}
            elements={listValues}
            error={uploadError}
            onAbort={onUploadAbort}
            onClose={OnUploadClose}
            isFinished={isFinishedUpload}
          />
        )}
      </PageLayout>
    </>
  );
};
