import { faFileAudio } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import React, { useState, useEffect } from "react";
import { Alert, ButtonGroup, Dropdown, Table } from "react-bootstrap";

import { Profile, ProfileVoiceType, Region, Entity, Servant, CraftEssence } from "@atlasacademy/api-connector";
import { toTitleCase } from "@atlasacademy/api-descriptor";

import Api from "../../Api";
import renderCollapsibleContent from "../../Component/CollapsibleContent";
import EntityDescriptor from "../../Descriptor/EntityDescriptor";
import ScriptDescriptor from "../../Descriptor/ScriptDescriptor";
import VoiceActorDescriptor from "../../Descriptor/VoiceActorDescriptor";
import VoiceCondTypeDescriptor from "../../Descriptor/VoiceCondTypeDescriptor";
import mergeVoiceLine from "../../Descriptor/VoiceLineMerger";
import VoiceLinePlayer from "../../Descriptor/VoiceLinePlayer";
import VoicePlayCondDescriptor from "../../Descriptor/VoicePlayCondDescriptor";
import VoicePrefixDescriptor from "../../Descriptor/VoicePrefixDescriptor";
import { mergeElements } from "../../Helper/OutputHelper";
import { VoiceSubtitleFormat } from "../../Helper/StringHelper";

import "../../Helper/StringHelper.css";

const voiceTextField = (region: Region, voiceType: ProfileVoiceType) => {
    return (
        (region === Region.JP && voiceType === ProfileVoiceType.FIRST_GET) ||
        region === Region.CN ||
        region === Region.TW
    );
};

export const VoiceLinesTable = ({
    region,
    voice,
    mergedDownloadNamePrefix,
    servants,
    costumes,
}: {
    region: Region;
    voice: Profile.VoiceGroup;
    mergedDownloadNamePrefix: string;
    servants: Map<number, Servant.ServantBasic>;
    costumes?: {
        [key: string]: Profile.CostumeDetail;
    };
}) => {
    const voiceLines = voice.voiceLines.sort((a, b) => (b.priority || 0) - (a.priority || 0));
    const voiceLineNames: string[] = [];
    const voiceNameCount: Record<string, number> = {};
    for (const line of voiceLines) {
        line.conds = line.conds.filter(
            (cond) => !(cond.condType === Profile.VoiceCondType.EVENT_END && cond.value === 0)
        );
        let lineName = line.overwriteName || line.name || "";
        if (lineName in voiceNameCount) {
            voiceNameCount[lineName]++;
        } else {
            voiceNameCount[lineName] = 1;
        }
        voiceLineNames.push(lineName.replace("{0}", voiceNameCount[lineName].toString()));
    }

    return (
        <Table bordered className="mb-0">
            <tbody>
                {voiceLines.map((line, index) => (
                    <tr key={`line_${index}`}>
                        <td style={{ verticalAlign: "middle" }}>
                            <b className="newline">{voiceLineNames[index]}</b>
                            <br />
                            <div className="newline">
                                {voiceTextField(region, voice.type) ? (
                                    line.text.map((line, i) => (
                                        <VoiceSubtitleFormat key={i} region={region} inputString={line} />
                                    ))
                                ) : (
                                    <VoiceSubtitleFormat region={region} inputString={line.subtitle} />
                                )}
                            </div>
                            {line.conds.length || line.playConds.length || line.summonScript ? (
                                <>
                                    <Alert variant="info" style={{ marginBottom: 0, marginTop: "1em" }}>
                                        {line.summonScript === undefined ? null : (
                                            <>
                                                Summoning Script:{" "}
                                                <ScriptDescriptor
                                                    region={region}
                                                    scriptId={line.summonScript.scriptId}
                                                    scriptType=""
                                                />
                                            </>
                                        )}
                                        {line.conds.length > 1 && (
                                            <>
                                                <b>Unlock Requirements (all of the following):</b>
                                                <br />
                                                <ul style={{ marginBottom: 0 }}>
                                                    {line.conds.map((cond, index) => (
                                                        <li key={index}>
                                                            <VoiceCondTypeDescriptor
                                                                region={region}
                                                                servants={servants}
                                                                costumes={costumes}
                                                                cond={cond}
                                                            />
                                                        </li>
                                                    ))}
                                                </ul>
                                            </>
                                        )}
                                        {line.conds.length === 1 && (
                                            <>
                                                <b>Unlock Requirement:</b>
                                                <br />
                                                <VoiceCondTypeDescriptor
                                                    region={region}
                                                    servants={servants}
                                                    costumes={costumes}
                                                    cond={line.conds[0]}
                                                />
                                                <br />
                                            </>
                                        )}
                                        <VoicePlayCondDescriptor
                                            region={region}
                                            playConds={line.playConds}
                                            servants={servants}
                                        />
                                    </Alert>
                                </>
                            ) : (
                                ""
                            )}
                        </td>
                        <td style={{ verticalAlign: "middle", width: "1px" }}>
                            <ButtonGroup>
                                <VoiceLinePlayer
                                    audioAssetUrls={line.audioAssets}
                                    delay={line.delay}
                                    title={voiceLineNames[index]}
                                />
                                <Dropdown as={ButtonGroup}>
                                    <Dropdown.Toggle variant={"info"} title={`Download ${voiceLineNames[index]}`}>
                                        <FontAwesomeIcon icon={faFileAudio} />
                                        &nbsp;
                                    </Dropdown.Toggle>

                                    <Dropdown.Menu title={`Download ${voiceLineNames[index]}`}>
                                        <Dropdown.Item
                                            title={`Download ${voiceLineNames[index]} merged file`}
                                            onClick={() => {
                                                const fileName = `${mergedDownloadNamePrefix} - ${voiceLineNames[index]}`;
                                                mergeVoiceLine(line.audioAssets, line.delay, fileName);
                                            }}
                                        >
                                            Merged
                                        </Dropdown.Item>
                                        {line.audioAssets.map((asset, i) => (
                                            <Dropdown.Item
                                                key={i}
                                                href={asset}
                                                target="_blank"
                                                title={`Download ${voiceLineNames[index]} part ${i + 1}`}
                                            >
                                                Part {i + 1}
                                            </Dropdown.Item>
                                        ))}
                                    </Dropdown.Menu>
                                </Dropdown>
                            </ButtonGroup>
                        </td>
                    </tr>
                ))}
            </tbody>
        </Table>
    );
};

export default function ServantVoiceLines(props: {
    region: Region;
    servants: Map<number, Servant.ServantBasic>;
    servant: Servant.Servant | CraftEssence.CraftEssence;
    servantName?: string;
}) {
    const [relatedVoiceSvts, setRelatedVoiceSvts] = useState<Entity.EntityBasic[] | null>(null);
    useEffect(() => {
        Api.searchEntityVoiceCondSvt([props.servant.collectionNo]).then((s) => setRelatedVoiceSvts(s));
    }, [props.servant]);

    const { profile, ascensionAdd } = props.servant;
    const voices = profile?.voices;
    const voicePrefixes = new Set([...(voices?.entries() || [])].map((entry) => entry[1].voicePrefix));
    const voicePrefixConditionPresent = voicePrefixes.size > 1;

    // sorting into prefixes
    const sortedVoice: [number, Profile.VoiceGroup[] | undefined][] = [...voicePrefixes].map((prefix) => [
        prefix,
        voices?.filter((voice) => voice.voicePrefix === prefix),
    ]);

    const voiceGroupTable = sortedVoice.map(([prefix, voices]) => {
        const outputTable = (
            <Table responsive>
                <thead>
                    <tr>
                        <td>Type</td>
                        <td>Lines</td>
                    </tr>
                </thead>
                <tbody>
                    {voices?.map((voice) => (
                        <tr key={`${voice.svtId}-${voice.type}-${voice.voicePrefix}`}>
                            <td>{voice.type === ProfileVoiceType.GROETH ? "Growth" : toTitleCase(voice.type)}</td>
                            <td>
                                <VoiceLinesTable
                                    region={props.region}
                                    voice={voice}
                                    mergedDownloadNamePrefix={`${props.servant.collectionNo} - ${props.servant.name}`}
                                    servants={props.servants}
                                    costumes={props.servant.profile?.costume}
                                />
                            </td>
                        </tr>
                    ))}
                </tbody>
            </Table>
        );
        if (voicePrefixConditionPresent) {
            const title = (
                <VoicePrefixDescriptor
                    currentVoicePrefix={prefix}
                    ascensionAdd={ascensionAdd}
                    costumes={profile?.costume}
                />
            );
            return (
                <React.Fragment key={prefix}>
                    {renderCollapsibleContent({ title: title, content: outputTable, subheader: false })}
                </React.Fragment>
            );
        } else {
            return <React.Fragment key={prefix}>{outputTable}</React.Fragment>;
        }
    });

    return (
        <>
            <Alert variant="success">
                <VoiceActorDescriptor region={props.region} cv={props.servant.profile?.cv} />
            </Alert>
            {props.servant.type !== Entity.EntityType.SERVANT_EQUIP && (
                <Alert variant="success">
                    {relatedVoiceSvts !== null
                        ? relatedVoiceSvts.length > 0
                            ? `Servants with voice lines about ${props.servantName ?? props.servant.name}: `
                            : `There is no voice line about ${
                                  props.servantName ?? props.servant.name
                              } from other servants.`
                        : "Fetching related voice line data ..."}
                    {relatedVoiceSvts !== null && relatedVoiceSvts.length > 0
                        ? mergeElements(
                              relatedVoiceSvts.map((svt) => (
                                  <EntityDescriptor key={svt.id} region={props.region} entity={svt} tab={"voices"} />
                              )),
                              ", "
                          )
                        : ""}
                </Alert>
            )}
            {voiceGroupTable}
        </>
    );
}