/* eslint-disable react/no-this-in-sfc */

import React, { useEffect, useState } from "react";
import {
  Collapse,
  Dropdown,
  DropdownButton,
  Form,
  Button,
  Spinner
} from "react-bootstrap";
import * as GQL from "src/core/generated-graphql";
import { StashService } from "src/core/StashService";
import {
  FilterSelect,
  StudioSelect,
  SceneGallerySelect,
  Modal,
  Icon
} from "src/components/Shared";
import { useToast } from "src/hooks";
import { ImageUtils } from "src/utils";

interface IProps {
  scene: GQL.SceneDataFragment;
  onUpdate: (scene: GQL.SceneDataFragment) => void;
  onDelete: () => void;
}

export const SceneEditPanel: React.FC<IProps> = (props: IProps) => {
  const Toast = useToast();
  const [title, setTitle] = useState<string>();
  const [details, setDetails] = useState<string>();
  const [url, setUrl] = useState<string>();
  const [date, setDate] = useState<string>();
  const [rating, setRating] = useState<number>();
  const [galleryId, setGalleryId] = useState<string>();
  const [studioId, setStudioId] = useState<string>();
  const [performerIds, setPerformerIds] = useState<string[]>();
  const [tagIds, setTagIds] = useState<string[]>();
  const [coverImage, setCoverImage] = useState<string>();

  const Scrapers = StashService.useListSceneScrapers();
  const [queryableScrapers, setQueryableScrapers] = useState<GQL.Scraper[]>([]);

  const [isDeleteAlertOpen, setIsDeleteAlertOpen] = useState<boolean>(false);
  const [deleteFile, setDeleteFile] = useState<boolean>(false);
  const [deleteGenerated, setDeleteGenerated] = useState<boolean>(true);

  const [isCoverImageOpen, setIsCoverImageOpen] = useState<boolean>(false);
  const [coverImagePreview, setCoverImagePreview] = useState<string>();

  // Network state
  const [isLoading, setIsLoading] = useState(false);

  const [updateScene] = StashService.useSceneUpdate(getSceneInput());
  const [deleteScene] = StashService.useSceneDestroy(getSceneDeleteInput());

  useEffect(() => {
    const newQueryableScrapers = (Scrapers?.data?.listSceneScrapers ?? []).filter(s => (
        s.scene?.supported_scrapes.includes(GQL.ScrapeType.Fragment)
    ));

    setQueryableScrapers(newQueryableScrapers);
  }, [Scrapers]);

  function updateSceneEditState(state: Partial<GQL.SceneDataFragment>) {
    const perfIds = state.performers
      ? state.performers.map(performer => performer.id)
      : undefined;
    const tIds = state.tags ? state.tags.map(tag => tag.id) : undefined;

    setTitle(state.title ?? undefined);
    setDetails(state.details ?? undefined);
    setUrl(state.url ?? undefined);
    setDate(state.date ?? undefined);
    setRating(state.rating === null ? NaN : state.rating);
    setGalleryId(state?.gallery?.id ?? undefined);
    setStudioId(state?.studio?.id ?? undefined);
    setPerformerIds(perfIds);
    setTagIds(tIds);
  }

  useEffect(() => {
    updateSceneEditState(props.scene);
    setCoverImagePreview(props.scene?.paths?.screenshot ?? undefined);
  }, [props.scene]);

  ImageUtils.usePasteImage(onImageLoad);

  function getSceneInput(): GQL.SceneUpdateInput {
    return {
      id: props.scene.id,
      title,
      details,
      url,
      date,
      rating,
      gallery_id: galleryId,
      studio_id: studioId,
      performer_ids: performerIds,
      tag_ids: tagIds,
      cover_image: coverImage
    };
  }

  async function onSave() {
    setIsLoading(true);
    try {
      const result = await updateScene();
      if(result.data?.sceneUpdate) {
        props.onUpdate(result.data.sceneUpdate);
        Toast.success({ content: "Updated scene" });
      }
    } catch (e) {
      Toast.error(e);
    }
    setIsLoading(false);
  }

  function getSceneDeleteInput(): GQL.SceneDestroyInput {
    return {
      id: props.scene.id,
      delete_file: deleteFile,
      delete_generated: deleteGenerated
    };
  }

  async function onDelete() {
    setIsDeleteAlertOpen(false);
    setIsLoading(true);
    try {
      await deleteScene();
      Toast.success({ content: "Deleted scene" });
    } catch (e) {
      Toast.error(e);
    }
    setIsLoading(false);

    props.onDelete();
  }

  function renderMultiSelect(
    type: "performers" | "tags",
    initialIds: string[] = []
  ) {
    return (
      <FilterSelect
        type={type}
        isMulti
        onSelect={items => {
          const ids = items.map(i => i.id);
          switch (type) {
            case "performers":
              setPerformerIds(ids);
              break;
            case "tags":
              setTagIds(ids);
              break;
          }
        }}
        initialIds={initialIds}
      />
    );
  }

  function renderDeleteAlert() {
    return (
      <Modal
        show={isDeleteAlertOpen}
        icon="trash-alt"
        header="Delete Scene?"
        accept={{ variant: "danger", onClick: onDelete, text: "Delete" }}
        cancel={{ onClick: () => setIsDeleteAlertOpen(false), text: "Cancel" }}
      >
        <p>
          Are you sure you want to delete this scene? Unless the file is also
          deleted, this scene will be re-added when scan is performed.
        </p>
        <Form>
          <Form.Check
            checked={deleteFile}
            label="Delete file"
            onChange={() => setDeleteFile(!deleteFile)}
          />
          <Form.Check
            checked={deleteGenerated}
            label="Delete generated supporting files"
            onChange={() => setDeleteGenerated(!deleteGenerated)}
          />
        </Form>
      </Modal>
    );
  }

  function onImageLoad(this: FileReader) {
    setCoverImagePreview(this.result as string);
    setCoverImage(this.result as string);
  }

  function onCoverImageChange(event: React.FormEvent<HTMLInputElement>) {
    ImageUtils.onImageChange(event, onImageLoad);
  }

  async function onScrapeClicked(
    scraper: GQL.Scraper
  ) {
    setIsLoading(true);
    try {
      const result = await StashService.queryScrapeScene(
        scraper.id,
        getSceneInput()
      );
      if (!result.data || !result.data.scrapeScene) {
        return;
      }
      updateSceneFromScrapedScene(result.data.scrapeScene);
    } catch (e) {
      Toast.error(e);
    } finally {
      setIsLoading(false);
    }
  }

  function renderScraperMenu() {
    if (!queryableScrapers || queryableScrapers.length === 0) {
      return;
    }

    return (
      <DropdownButton id="scene-scrape" title="Scrape with...">
        {queryableScrapers.map(s => (
          <Dropdown.Item onClick={() => onScrapeClicked(s)}>
            {s.name}
          </Dropdown.Item>
        ))}
      </DropdownButton>
    );
  }

  function urlScrapable(scrapedUrl: string): boolean {
    return (Scrapers?.data?.listSceneScrapers ?? []).some(s =>
      (s?.scene?.urls ?? []).some(u => scrapedUrl.includes(u))
    );
  }

  function updateSceneFromScrapedScene(scene: GQL.ScrapedSceneDataFragment) {
    if (!title && scene.title) {
      setTitle(scene.title);
    }

    if (!details && scene.details) {
      setDetails(scene.details);
    }

    if (!date && scene.date) {
      setDate(scene.date);
    }

    if (!url && scene.url) {
      setUrl(scene.url);
    }

    if (!studioId && scene.studio && scene.studio.id) {
      setStudioId(scene.studio.id);
    }

    if (
      (!performerIds || performerIds.length === 0) &&
      scene.performers &&
      scene.performers.length > 0
    ) {
      const idPerfs = scene.performers.filter(p => {
        return p.id !== undefined && p.id !== null;
      });

      if (idPerfs.length > 0) {
        const newIds = idPerfs.map(p => p.id);
        setPerformerIds(newIds as string[]);
      }
    }

    if (
      (!tagIds || tagIds.length === 0) &&
      scene.tags &&
      scene.tags.length > 0
    ) {
      const idTags = scene.tags.filter(p => {
        return p.id !== undefined && p.id !== null;
      });

      if (idTags.length > 0) {
        const newIds = idTags.map(p => p.id);
        setTagIds(newIds as string[]);
      }
    }
  }

  async function onScrapeSceneURL() {
    if (!url) {
      return;
    }
    setIsLoading(true);
    try {
      const result = await StashService.queryScrapeSceneURL(url);
      if (!result.data || !result.data.scrapeSceneURL) {
        return;
      }
      updateSceneFromScrapedScene(result.data.scrapeSceneURL);
    } catch (e) {
      Toast.error(e);
    } finally {
      setIsLoading(false);
    }
  }

  function maybeRenderScrapeButton() {
    if (!url || !urlScrapable(url)) {
      return undefined;
    }
    return (
      <Button id="scrape-url-button" onClick={onScrapeSceneURL}>
        <Icon icon="file-download" />
      </Button>
    );
  }

  return (
    <>
      {renderDeleteAlert()}
      {isLoading ? <Spinner animation="border" variant="light" /> : undefined}
      <div className="form-container " style={{ width: "50%" }}>
        <Form.Group controlId="title">
          <Form.Label>Title</Form.Label>
          <Form.Control
            onChange={(newValue: any) => setTitle(newValue.target.value)}
            value={title}
          />
        </Form.Group>

        <Form.Group controlId="details">
          <Form.Label>Details</Form.Label>
          <Form.Control
            as="textarea"
            onChange={(newValue: any) => setDetails(newValue.target.value)}
            value={details}
          />
        </Form.Group>

        <Form.Group controlId="url">
          <Form.Label>URL</Form.Label>
          <Form.Control
            onChange={(newValue: any) => setUrl(newValue.target.value)}
            value={url}
          />
          {maybeRenderScrapeButton()}
        </Form.Group>

        <Form.Group controlId="date">
          <Form.Label>Date</Form.Label>
          <Form.Control
            onChange={(newValue: any) => setDate(newValue.target.value)}
            value={date}
          />
          <div>YYYY-MM-DD</div>
        </Form.Group>

        <Form.Group controlId="rating">
          <Form.Label>Rating</Form.Label>
          <Form.Control
            as="select"
            onChange={(event: any) =>
              setRating(parseInt(event.target.value, 10))
            }
          >
            {["", 1, 2, 3, 4, 5].map(opt => (
              <option selected={opt === rating} value={opt}>
                {opt}
              </option>
            ))}
          </Form.Control>
        </Form.Group>

        <Form.Group controlId="gallery">
          <Form.Label>Gallery</Form.Label>
          <SceneGallerySelect
            sceneId={props.scene.id}
            initialId={galleryId}
            onSelect={item => setGalleryId(item ? item.id : undefined)}
          />
        </Form.Group>

        <Form.Group controlId="studio">
          <Form.Label>Studio</Form.Label>
          <StudioSelect
            onSelect={items => items.length && setStudioId(items[0]?.id)}
            initialIds={studioId ? [studioId] : []}
          />
        </Form.Group>

        <Form.Group controlId="performers">
          <Form.Label>Performers</Form.Label>
          {renderMultiSelect("performers", performerIds)}
        </Form.Group>

        <Form.Group controlId="tags">
          <Form.Label>Tags</Form.Label>
          {renderMultiSelect("tags", tagIds)}
        </Form.Group>

        <div>
          <Button
            variant="link"
            onClick={() => setIsCoverImageOpen(!isCoverImageOpen)}
          >
            <Icon icon={isCoverImageOpen ? "chevron-down" : "chevron-right"} />
            <span>Cover Image</span>
          </Button>
          <Collapse in={isCoverImageOpen}>
            <div>
              <img className="scene-cover" src={coverImagePreview} alt="" />
              <Form.Group className="test" controlId="cover">
                <Form.Control
                  type="file"
                  onChange={onCoverImageChange}
                  accept=".jpg,.jpeg,.png"
                />
              </Form.Group>
            </div>
          </Collapse>
        </div>
      </div>
      <Button className="edit-button" variant="primary" onClick={onSave}>
        Save
      </Button>
      <Button
        className="edit-button"
        variant="danger"
        onClick={() => setIsDeleteAlertOpen(true)}
      >
        Delete
      </Button>
      {renderScraperMenu()}
    </>
  );
};
