import React, { useEffect, useState } from "react";
import { FormattedMessage, useIntl } from "react-intl";
import cx from "classnames";
import { IconDefinition } from "@fortawesome/fontawesome-svg-core";

import * as GQL from "src/core/generated-graphql";
import { useFindStudio } from "src/core/StashService";
import { Icon } from "src/components/Shared/Icon";
import { ModalComponent } from "src/components/Shared/Modal";
import { faCheck, faTimes } from "@fortawesome/free-solid-svg-icons";
import { Button, Form } from "react-bootstrap";
import { TruncatedText } from "src/components/Shared/TruncatedText";
import { excludeFields } from "src/utils/data";
import { ExternalLink } from "src/components/Shared/ExternalLink";
import { StashIDPill } from "src/components/Shared/StashID";

interface IStudioDetailsProps {
  studio: GQL.ScrapedSceneStudioDataFragment;
  endpoint?: string;
  excluded: Record<string, boolean>;
  toggleField: (field: string) => void;
  isNew?: boolean;
}

const StudioDetails: React.FC<IStudioDetailsProps> = ({
  studio,
  endpoint,
  excluded,
  toggleField,
  isNew = false,
}) => {
  function maybeRenderImage() {
    if (!studio.image) return;

    return (
      <div className="row">
        <div className="col-12 image-selection">
          <div className="studio-image">
            <Button
              onClick={() => toggleField("image")}
              variant="secondary"
              className={cx(
                "studio-image-exclude",
                excluded.image ? "text-muted" : "text-success"
              )}
            >
              <Icon icon={excluded.image ? faTimes : faCheck} />
            </Button>
            <img src={studio.image} alt="" />
          </div>
        </div>
      </div>
    );
  }

  function maybeRenderField(
    id: string,
    text: string | null | undefined,
    isSelectable: boolean = true,
    messageId?: string
  ) {
    if (!text) return;
    if (!messageId) messageId = id;

    return (
      <div className="row no-gutters">
        <div className="col-5 create-modal-field" key={id}>
          {isSelectable && (
            <Button
              onClick={() => toggleField(id)}
              variant="secondary"
              className={excluded[id] ? "text-muted" : "text-success"}
            >
              <Icon icon={excluded[id] ? faTimes : faCheck} />
            </Button>
          )}
          <strong>
            <FormattedMessage id={messageId} />:
          </strong>
        </div>
        <TruncatedText className="col-7" text={text} />
      </div>
    );
  }

  function maybeRenderURLListField(
    name: string,
    text: string[] | null | undefined,
    truncate: boolean = true
  ) {
    if (!text) return;

    return (
      <div className="row no-gutters">
        <div className="col-5 create-modal-field" key={name}>
          {!isNew && (
            <Button
              onClick={() => toggleField(name)}
              variant="secondary"
              className={excluded[name] ? "text-muted" : "text-success"}
            >
              <Icon icon={excluded[name] ? faTimes : faCheck} />
            </Button>
          )}
          <strong>
            <FormattedMessage id={name} />:
          </strong>
        </div>
        <div className="col-7 create-modal-value">
          <ul>
            {text.map((t, i) => (
              <li key={i}>
                <ExternalLink href={t}>
                  {truncate ? <TruncatedText text={t} /> : t}
                </ExternalLink>
              </li>
            ))}
          </ul>
        </div>
      </div>
    );
  }

  function maybeRenderStashBoxLink() {
    const base = endpoint?.match(/https?:\/\/.*?\//)?.[0];
    if (!base || !studio.remote_site_id) return;

    return (
      <StashIDPill
        linkType="studios"
        stashID={{ endpoint: endpoint, stash_id: studio.remote_site_id }}
      />
    );
  }

  return (
    <div>
      {maybeRenderImage()}
      <div className="row">
        <div className="col-12">
          {maybeRenderField("name", studio.name, !isNew)}
          {maybeRenderURLListField("urls", studio.urls)}
          {maybeRenderField("details", studio.details)}
          {maybeRenderField("aliases", studio.aliases)}
          {maybeRenderField("tags", studio.tags?.map((t) => t.name).join(", "))}
          {maybeRenderField(
            "parent_id",
            studio.parent?.name,
            true,
            "parent_studio"
          )}
          {maybeRenderStashBoxLink()}
        </div>
      </div>
    </div>
  );
};

interface IStudioModalProps {
  studio: GQL.ScrapedSceneStudioDataFragment;
  modalVisible: boolean;
  closeModal: () => void;
  handleStudioCreate: (
    input: GQL.StudioCreateInput,
    parent?: GQL.StudioCreateInput
  ) => void;
  excludedStudioFields?: string[];
  header: string;
  icon: IconDefinition;
  endpoint?: string;
}

const StudioModal: React.FC<IStudioModalProps> = ({
  modalVisible,
  studio,
  handleStudioCreate,
  closeModal,
  excludedStudioFields = [],
  header,
  icon,
  endpoint,
}) => {
  const intl = useIntl();

  const [excluded, setExcluded] = useState<Record<string, boolean>>(
    excludedStudioFields.reduce(
      (dict, field) => ({ ...dict, [field]: true }),
      {}
    )
  );
  const toggleField = (name: string) =>
    setExcluded({
      ...excluded,
      [name]: !excluded[name],
    });

  const [parentExcluded, setParentExcluded] = useState<Record<string, boolean>>(
    excludedStudioFields.reduce(
      (dict, field) => ({ ...dict, [field]: true }),
      {}
    )
  );
  const toggleParentField = (name: string) =>
    setParentExcluded({
      ...parentExcluded,
      [name]: !parentExcluded[name],
    });

  const [createParentStudio, setCreateParentStudio] = useState<boolean>(
    !!studio.parent
  );

  useEffect(() => {
    setCreateParentStudio(!excluded.parent_id && !!studio.parent);
  }, [excluded.parent_id, studio.parent]);

  let sendParentStudio = true;
  // The parent studio exists, need to check if it has a Stash ID.
  const queryResult = useFindStudio(studio.parent?.stored_id ?? "");
  if (
    queryResult.data?.findStudio?.stash_ids?.length &&
    queryResult.data?.findStudio?.stash_ids?.length > 0
  ) {
    // It already has a Stash ID, so we can skip worrying about it
    sendParentStudio = false;
  }

  const parentStudioCreateText = () => {
    if (studio.parent && studio.parent.stored_id) {
      return "actions.assign_stashid_to_parent_studio";
    }
    return "actions.create_parent_studio";
  };

  function onSave() {
    if (!studio.name) {
      throw new Error("studio name must set");
    }

    const studioData: GQL.StudioCreateInput = {
      name: studio.name,
      urls: studio.urls,
      image: studio.image,
      parent_id: studio.parent?.stored_id,
      details: studio.details,
      aliases: studio.aliases
        ?.split(",")
        .map((a) => a.trim())
        .filter((a) => a),
      tag_ids: studio.tags?.map((t) => t.stored_id).filter((id) => id) as
        | string[]
        | undefined,
    };

    // stashid handling code
    const remoteSiteID = studio.remote_site_id;
    const timeNow = new Date().toISOString();
    if (remoteSiteID && endpoint) {
      studioData.stash_ids = [
        {
          endpoint,
          stash_id: remoteSiteID,
          updated_at: timeNow,
        },
      ];
    }

    // handle exclusions
    excludeFields(studioData, excluded);

    let parentData: GQL.StudioCreateInput | undefined = undefined;

    if (createParentStudio && sendParentStudio) {
      if (!studio.parent?.name) {
        throw new Error("parent studio name must set");
      }

      parentData = {
        name: studio.parent?.name,
        urls: studio.parent?.urls,
        image: studio.parent?.image,
        details: studio.parent?.details,
        aliases: studio.parent?.aliases
          ?.split(",")
          .map((a) => a.trim())
          .filter((a) => a),
        tag_ids: studio.parent?.tags
          ?.map((t) => t.stored_id)
          .filter((id) => id) as string[] | undefined,
      };

      // stashid handling code
      const parentRemoteSiteID = studio.parent?.remote_site_id;
      if (parentRemoteSiteID && endpoint) {
        parentData.stash_ids = [
          {
            endpoint,
            stash_id: parentRemoteSiteID,
            updated_at: timeNow,
          },
        ];
      }

      // handle exclusions
      // Can't exclude parent studio name when creating a new one
      parentExcluded.name = false;
      excludeFields(parentData, parentExcluded);
    }

    handleStudioCreate(studioData, parentData);
  }

  function maybeRenderParentStudio() {
    // There is no parent studio or it already has a Stash ID
    if (!studio.parent || !sendParentStudio || excluded.parent_id) {
      return;
    }

    // force create if there is no current parent studio and parent studio is not excluded
    const mustCreateParent = !studio.parent.stored_id;

    return (
      <div>
        <Form.Group className="mb-4 mt-4">
          <Form.Check
            id="create-parent"
            checked={createParentStudio}
            label={intl.formatMessage({
              id: parentStudioCreateText(),
            })}
            disabled={mustCreateParent}
            onChange={() => setCreateParentStudio(!createParentStudio)}
          />
        </Form.Group>
        {maybeRenderParentStudioDetails()}
      </div>
    );
  }

  function maybeRenderParentStudioDetails() {
    if (!createParentStudio || !studio.parent) {
      return;
    }

    return (
      <StudioDetails
        studio={studio.parent}
        excluded={parentExcluded}
        toggleField={(field) => toggleParentField(field)}
        endpoint={endpoint}
        isNew
      />
    );
  }

  return (
    <ModalComponent
      show={modalVisible}
      accept={{
        text: intl.formatMessage({ id: "actions.save" }),
        onClick: onSave,
      }}
      cancel={{ onClick: () => closeModal(), variant: "secondary" }}
      onHide={() => closeModal()}
      dialogClassName="studio-create-modal"
      icon={icon}
      header={header}
    >
      <StudioDetails
        studio={studio}
        excluded={excluded}
        toggleField={(field) => toggleField(field)}
        endpoint={endpoint}
      />

      {maybeRenderParentStudio()}
    </ModalComponent>
  );
};

export default StudioModal;
