import { AxiosError } from "axios";
import React from "react";
import { Col, Row, Tab, Table, Tabs } from "react-bootstrap";
import { withRouter } from "react-router";
import { RouteComponentProps } from "react-router-dom";

import { Event, Item, Region, Mission, Quest, Servant, EnumList, War } from "@atlasacademy/api-connector";

import Api, { Host } from "../Api";
import DataTable from "../Component/DataTable";
import ErrorStatus from "../Component/ErrorStatus";
import Loading from "../Component/Loading";
import RawDataViewer from "../Component/RawDataViewer";
import GiftDescriptor from "../Descriptor/GiftDescriptor";
import MissionConditionDescriptor from "../Descriptor/MissionConditionDescriptor";
import WarDescriptor from "../Descriptor/WarDescriptor";
import { replacePUACodePoints } from "../Helper/StringHelper";
import { getEventStatus } from "../Helper/TimeHelper";
import Manager from "../Setting/Manager";
import EventLottery from "./Event/EventLottery";
import EventReward from "./Event/EventReward";
import EventRewardTower from "./Event/EventRewardTower";
import EventTreasureBoxes from "./Event/EventTreasureBoxes";
import EventVoices from "./Event/EventVoices";
import ShopTab from "./Event/Shop";

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

interface TabInfo {
    type: "ladder" | "shop" | "mission" | "tower" | "lottery" | "treasureBox";
    id: number;
    title: string | React.ReactNode;
    tabKey: string;
}

interface IProps extends RouteComponentProps {
    region: Region;
    eventId: number;
    tab?: string;
}

interface IState {
    error?: AxiosError;
    loading: boolean;
    event?: Event.Event;
    wars: War.War[];
    servantCache: Map<number, Servant.ServantBasic>;
    itemCache: Map<number, Item.Item>;
    questCache: Map<number, Quest.Quest>;
    enums?: EnumList;
    missionRefs: Map<number, React.Ref<any>>;

    // shop slot => (item ID, set count)
    shopFilters: Map<number, Map<number, number>>;
}

class EventPage extends React.Component<IProps, IState> {
    constructor(props: IProps) {
        super(props);

        this.state = {
            loading: true,
            wars: [] as War.War[],
            servantCache: new Map(),
            itemCache: new Map(),
            questCache: new Map(),
            missionRefs: new Map(),
            shopFilters: new Map(),
        };
    }

    async componentDidMount() {
        Manager.setRegion(this.props.region);
        this.loadEvent();
    }

    loadEnums() {
        Api.enumList().then((enums) => this.setState({ enums }));
    }

    loadServantMap() {
        Api.servantList().then((servantList) => {
            this.setState({
                servantCache: new Map(servantList.map((servant) => [servant.id, servant])),
            });
        });
    }

    loadItemMap() {
        Api.itemList().then((itemList) => {
            this.setState({
                itemCache: new Map(itemList.map((item) => [item.id, item])),
            });
        });
    }

    loadWars(warIds: number[], setQuestCache = false) {
        Promise.all(warIds.map((warId) => Api.war(warId))).then((wars) => {
            this.setState({ wars });
            if (setQuestCache) {
                const questCache = new Map<number, Quest.Quest>();
                for (const war of wars) {
                    for (const spot of war.spots) {
                        for (const quest of spot.quests) {
                            questCache.set(quest.id, quest);
                        }
                    }
                }
                this.setState({ questCache });
            }
        });
    }

    loadEvent() {
        Api.event(this.props.eventId)
            .then((event) => {
                document.title = `[${this.props.region}] Event - ${event.name} - Atlas Academy DB`;
                this.setState({
                    event: event,
                    loading: false,
                    missionRefs: new Map(event.missions.map((mission) => [mission.id, React.createRef()])),
                });
                if (
                    event.towers.length > 0 ||
                    event.rewards.length > 0 ||
                    event.shop.length > 0 ||
                    event.missions.length > 0 ||
                    event.lotteries.length > 0 ||
                    event.treasureBoxes.length > 0
                ) {
                    this.loadItemMap();
                }
                this.loadWars(event.warIds, event.missions.length > 0 || event.shop.length > 0);
                if (event.missions.length > 0 || event.voices.length > 0) {
                    this.loadServantMap();
                }
                if (event.missions.length > 0) {
                    this.loadEnums();
                }
            })
            .catch((error) => this.setState({ error }));
    }

    renderMissionConds(
        region: Region,
        mission: Mission.Mission,
        missionMap: Map<number, Mission.Mission>,
        servantCache: Map<number, Servant.ServantBasic>,
        itemCache: Map<number, Item.Item>,
        questCache: Map<number, Quest.Quest>,
        warIds?: number[],
        enums?: EnumList
    ) {
        const scrollToMissions = (id: number) => {
            let elementRef = this.state.missionRefs.get(id) as React.RefObject<HTMLDivElement>;
            elementRef?.current?.scrollIntoView({ behavior: "smooth" });
        };
        return [Mission.ProgressType.OPEN_CONDITION, Mission.ProgressType.START, Mission.ProgressType.CLEAR].map(
            (progressType) => {
                const conds = mission.conds.filter((cond) => cond.missionProgressType === progressType);
                if (conds.length > 0) {
                    return (
                        <MissionConditionDescriptor
                            key={conds[0].id}
                            region={region}
                            cond={conds[0]}
                            quests={questCache}
                            servants={servantCache}
                            missions={missionMap}
                            items={itemCache}
                            enums={enums}
                            warIds={warIds}
                            handleNavigateMissionId={scrollToMissions}
                        />
                    );
                } else {
                    return null;
                }
            }
        );
    }

    renderMissionRow(
        region: Region,
        mission: Mission.Mission,
        missionMap: Map<number, Mission.Mission>,
        servantCache: Map<number, Servant.ServantBasic>,
        itemCache: Map<number, Item.Item>,
        questCache: Map<number, Quest.Quest>,
        warIds?: number[],
        enums?: EnumList
    ) {
        return (
            <>
                <th scope="row" style={{ textAlign: "center" }} ref={this.state.missionRefs.get(mission.id)}>
                    {mission.dispNo}
                </th>
                <td>
                    <b className="newline">{mission.name}</b>
                    <br />
                    {this.renderMissionConds(
                        region,
                        mission,
                        missionMap,
                        servantCache,
                        itemCache,
                        questCache,
                        warIds,
                        enums
                    )}
                </td>
                <td>
                    {mission.gifts.map((gift) => (
                        <div key={`${gift.objectId}-${gift.priority}`}>
                            <GiftDescriptor region={region} gift={gift} servants={servantCache} items={itemCache} />
                            <br />
                        </div>
                    ))}
                </td>
            </>
        );
    }

    renderMissionTab(
        region: Region,
        missions: Mission.Mission[],
        servantCache: Map<number, Servant.ServantBasic>,
        itemCache: Map<number, Item.Item>,
        questCache: Map<number, Quest.Quest>,
        warIds?: number[],
        enums?: EnumList
    ) {
        const missionMap = new Map(missions.map((mission) => [mission.id, mission]));
        return (
            <Table hover responsive className="mission-table">
                <thead>
                    <tr>
                        <th style={{ textAlign: "center" }}>#</th>
                        <th>Detail</th>
                        <th>Reward</th>
                    </tr>
                </thead>
                <tbody>
                    {missions
                        .sort((a, b) => a.dispNo - b.dispNo)
                        .map((mission) => {
                            return (
                                <tr key={mission.id}>
                                    {this.renderMissionRow(
                                        region,
                                        mission,
                                        missionMap,
                                        servantCache,
                                        itemCache,
                                        questCache,
                                        warIds,
                                        enums
                                    )}
                                </tr>
                            );
                        })}
                </tbody>
            </Table>
        );
    }

    renderTab(
        region: Region,
        event: Event.Event,
        tab: TabInfo,
        servantCache: Map<number, Servant.ServantBasic>,
        itemCache: Map<number, Item.Item>,
        questCache: Map<number, Quest.Quest>,
        enums?: EnumList
    ) {
        switch (tab.type) {
            case "tower":
                const tower = event.towers.find((tower) => tower.towerId === tab.id);
                if (tower !== undefined) {
                    return <EventRewardTower region={region} tower={tower} itemMap={itemCache} />;
                } else {
                    return null;
                }
            case "ladder":
                return (
                    <EventReward
                        region={region}
                        rewards={event.rewards.filter((reward) => reward.groupId === tab.id)}
                        allPointBuffs={event.pointBuffs}
                        itemMap={itemCache}
                    />
                );
            case "shop":
                let { shopFilters } = this.state;
                return (
                    <ShopTab
                        region={region}
                        shops={event.shop.filter((shop) => shop.slot === tab.id)}
                        itemCache={itemCache}
                        filters={shopFilters.get(tab.id) ?? new Map()}
                        onChange={(records) => {
                            shopFilters.set(tab.id, records);
                            this.setState({ shopFilters });
                        }}
                        questCache={questCache}
                    />
                );
            case "mission":
                return this.renderMissionTab(
                    region,
                    event.missions,
                    servantCache,
                    itemCache,
                    questCache,
                    event.warIds,
                    enums
                );
            case "lottery":
                const lottery = event.lotteries.find((lottery) => lottery.id === tab.id)!;
                return <EventLottery region={region} lottery={lottery} itemMap={itemCache} />;
            case "treasureBox":
                const treasureBoxes = event.treasureBoxes.filter((tb) => tb.slot === tab.id);
                return <EventTreasureBoxes region={region} treasureBoxes={treasureBoxes} itemCache={itemCache} />;
        }
    }

    render() {
        if (this.state.error) return <ErrorStatus error={this.state.error} />;

        if (this.state.loading || !this.state.event) return <Loading />;

        const event = this.state.event;

        let tabs: TabInfo[] = [];

        if (event.missions.length > 0) {
            tabs.push({
                type: "mission",
                id: 0,
                title: "Missions",
                tabKey: "missions",
            });
        }

        const lotteries = new Set(event.lotteries.map((lottery) => lottery.id));

        tabs = tabs.concat(
            event.lotteries
                .sort((a, b) => a.id - b.id)
                .map((lottery) => {
                    return {
                        type: "lottery",
                        id: lottery.id,
                        title: lotteries.size === 1 ? "Lottery" : `Lottery ${lottery.id}`,
                        tabKey: `lottery-${lottery.id}`,
                    };
                })
        );

        const towers = new Set(event.towers.map((tower) => tower.towerId));

        tabs = tabs.concat(
            event.towers
                .sort((a, b) => a.towerId - b.towerId)
                .map((tower) => {
                    return {
                        type: "tower",
                        id: tower.towerId,
                        title: towers.size === 1 ? "Tower" : tower.name,
                        tabKey: `tower-${tower.towerId}`,
                    };
                })
        );

        const pointGroupMap = new Map(event.pointGroups.map((pointGroup) => [pointGroup.groupId, pointGroup]));

        tabs = tabs.concat(
            Array.from(new Set(event.rewards.map((reward) => reward.groupId)))
                .sort((a, b) => a - b)
                .map((groupId) => {
                    let title: string | React.ReactNode = `Ladder ${groupId}`;
                    const pointGroupInfo = pointGroupMap.get(groupId);
                    if (groupId === 0) {
                        title = "Ladder";
                    } else if (pointGroupInfo !== undefined) {
                        title = (
                            <>
                                <img
                                    style={{ height: "1.75em" }}
                                    src={pointGroupInfo.icon}
                                    alt={`${pointGroupInfo.name} Icon`}
                                />
                                {pointGroupInfo.name}
                            </>
                        );
                    }
                    return {
                        type: "ladder",
                        id: groupId,
                        title: title,
                        tabKey: `ladder-${groupId}`,
                    };
                })
        );

        const treasureBoxSlots = Array.from(new Set(event.treasureBoxes.map((tb) => tb.slot)));

        tabs = tabs.concat(
            treasureBoxSlots
                .sort((a, b) => a - b)
                .map((slot) => {
                    return {
                        type: "treasureBox",
                        id: slot,
                        title: treasureBoxSlots.length === 1 ? "Treasure Box" : `Treasure Box ${slot}`,
                        tabKey: `treasure-box-${slot}`,
                    };
                })
        );

        const shopSlots = Array.from(new Set(event.shop.map((shop) => shop.slot)));

        tabs = tabs.concat(
            shopSlots
                .sort((a, b) => a - b)
                .map((shopSlot) => {
                    return {
                        type: "shop",
                        id: shopSlot,
                        title: shopSlots.length === 1 ? "Shop" : `Shop ${shopSlot}`,
                        tabKey: `shop-${shopSlot}`,
                    };
                })
        );

        const wars =
            this.state.wars.length === 1 ? (
                <WarDescriptor region={this.props.region} war={this.state.wars[0]} />
            ) : (
                <ul className="mb-0">
                    {this.state.wars.map((war) => (
                        <li key={war.id}>
                            <WarDescriptor region={this.props.region} war={war} />
                        </li>
                    ))}
                </ul>
            );

        return (
            <div>
                <h1>{replacePUACodePoints(event.name)}</h1>

                <br />
                <div style={{ marginBottom: "3%" }}>
                    <DataTable
                        data={{
                            ID: event.id,
                            Name: replacePUACodePoints(event.name),
                            ...(event.name !== event.originalName && { "Original Name": event.originalName }),
                            Wars: wars,
                            Status: getEventStatus(event.startedAt, event.endedAt),
                            Start: new Date(event.startedAt * 1000).toLocaleString(),
                            End: new Date(event.endedAt * 1000).toLocaleString(),
                            Raw: (
                                <Row>
                                    <Col>
                                        <RawDataViewer text="Nice" data={event} />
                                    </Col>
                                    <Col>
                                        <RawDataViewer
                                            text="Raw"
                                            data={`${Host}/raw/${this.props.region}/event/${event.id}`}
                                        />
                                    </Col>
                                </Row>
                            ),
                        }}
                    />
                </div>

                <Tabs
                    id={"event-reward-tabs"}
                    defaultActiveKey={this.props.tab ?? (tabs.length > 0 ? tabs[0].tabKey : undefined)}
                    mountOnEnter={true}
                    onSelect={(key: string | null) => {
                        this.props.history.replace(`/${this.props.region}/event/${this.props.eventId}/${key}`);
                    }}
                >
                    {tabs.map((tab) => {
                        return (
                            <Tab key={tab.tabKey} eventKey={tab.tabKey} title={tab.title}>
                                {this.renderTab(
                                    this.props.region,
                                    event,
                                    tab,
                                    this.state.servantCache,
                                    this.state.itemCache,
                                    this.state.questCache,
                                    this.state.enums
                                )}
                            </Tab>
                        );
                    })}
                    {event.voices.length > 0 && (
                        <Tab eventKey="voices" title="Voices">
                            <EventVoices
                                region={this.props.region}
                                voiceGroups={event.voices}
                                servants={this.state.servantCache}
                                eventRewardScenes={this.state.event.rewardScenes}
                            />
                        </Tab>
                    )}
                </Tabs>
            </div>
        );
    }
}

export default withRouter(EventPage);