import { Heading, Icon, Span, Stack, Text, Timeline} from '@chakra-ui/react';
import React, { Fragment, useMemo } from "react";
import { BsExclamationTriangle, BsSkipForward, BsStoplights } from "react-icons/bs";
import { MdMusicNote } from "react-icons/md";
import type {JsonPlayObject, LifecycleStep} from "../../core/Atomic";
import { patchObject } from "../../core/DataUtils";
import { timelineIconProps, timelineTextFormatting } from "../utils/ComponentUtils";
import { ChakraCodeBlockShort } from "./CodeBlock";
import { ErrorAlert } from "./ErrorAlert";
import { MSCollapsible, type MSCollapsibleExternalProps } from "./MSCollapsible";
import { PlayData } from "./PlayData";
import { Muted } from "./Typography";
import { JsonDiffPatch } from "./diffs/JsonDiff";

export interface LifeycleStepsTimelineProps extends MSCollapsibleExternalProps {
    steps: LifecycleStep[]
    original: JsonPlayObject
}

export const diffElements = (original: JsonPlayObject, steps: LifecycleStep[]): [React.JSX.Element[], JsonPlayObject?] => {

    const currentPlay: JsonPlayObject = structuredClone(original); // JSON.parse(JSON.stringify(original));
    let patchFailed = false;

    const diffElements: React.JSX.Element[] | null = [];
    let index = 0;

    for (const step of steps) {
        index++;
        const {
            patch,
            error
        } = step;

        if (patch === undefined) {
            if (error !== undefined) {
                diffElements.push(null);
            } else {
                diffElements.push(<Text>Play was identical after Transform.</Text>);
            }
            continue;
        }

        if (patchFailed) {
            diffElements.push(<ChakraCodeBlockShort key={`diffblockfallback-${index}`} title="Diff Patch" code={patch} />);
            continue;
        }
        const left: JsonPlayObject = structuredClone(currentPlay); // JSON.parse(JSON.stringify(currentPlay));
        left.data.meta = {
            ...(left.data.meta ?? {}),
            brainz: {
                ...(left.data.meta?.brainz ?? {})
            }
        }
        currentPlay.data.meta = {
            ...(currentPlay.data.meta ?? {}),
            brainz: {
                ...(currentPlay.data.meta?.brainz ?? {})
            }
        }

        try {
            currentPlay.data = patchObject(currentPlay.data, patch)// jdiff.patch(currentPlay, patch) as JsonPlayObject;
            diffElements.push(
                <JsonDiffPatch left={left.data} right={currentPlay.data}/>
            )
        } catch (e) {
            diffElements.push(<Fragment><ErrorAlert error={e} /><ChakraCodeBlockShort title="Diff Patch" key={`diffblockfallback-${index}`} code={patch} /></Fragment>);
            patchFailed = true;
        }

    }

    return [diffElements, patchFailed !== undefined ? currentPlay : undefined]
}

export const TransformSteps = (props: LifeycleStepsTimelineProps) => {
    const {
        steps,
        original,
        collapsibleOpen
    } = props;

    const [diffs, finalPlay] = useMemo(() => diffElements(original, steps), [steps, original]);

    return (
        <Timeline.Root  variant="subtle" size="lg" css={{ "--timeline-separator-display": 'block' }}>
            {steps.map((x, index) => {
                const {
                    patch,
                    inputs,
                    source,
                    error,
                    flowKnownState,
                    flowReason,
                    flowResult,
                    stageName,
                    stageType,
                    hook
                } = x;

                let timelineIcon: React.JSX.Element,
                iconProps: Record<string, any>,
                summary: React.JSX.Element,
                alertStatus: "error" | "info" | "warning" | "success" | "neutral";
                if(error === undefined && error !== null) {
                    timelineIcon = <BsStoplights/>;
                    iconProps = flowResult === 'continue' ? {color: "green.focusRing"} : {color: "red.focusRing"};
                    summary = <Fragment><Muted>was</Muted> completed{flowResult === 'stop' ? <Fragment><Muted> and </Muted> stopped <Muted> due to onSuccess condition</Muted></Fragment> : null}</Fragment>;
                    if(patch === undefined) {
                        summary = <Fragment>{summary}<Muted> with</Muted> no change <Muted>to Play</Muted></Fragment>;
                    }
                } else {
                    if(flowKnownState === 'skip') {
                        timelineIcon = <BsSkipForward/>;
                        summary = <Fragment><Muted>was</Muted> skipped.</Fragment>;
                        alertStatus = 'info';
                    } else if(flowKnownState === 'prereq') {
                        timelineIcon = <BsExclamationTriangle/>;
                        iconProps = {color: "orange.focusRing"};
                        summary = <Fragment><Muted>was</Muted> not completed <Muted>due to</Muted> prerequisite failure.</Fragment>;
                        alertStatus = "warning";
                    } else {
                        if(flowResult === 'continue') {
                            timelineIcon = <BsStoplights/>;
                            iconProps = {color: "orange.focusRing"};
                            summary = <Fragment>encountered an error <Muted>but</Muted> will continue <Muted>due to onFailure condition.</Muted></Fragment>;
                        } else {
                            timelineIcon = <BsExclamationTriangle/>;
                            iconProps = {color: "red.focusRing"};
                            summary = <Fragment>encountered an error.</Fragment>
                        }
                    }
                }
                
                return <Timeline.Item key={index}>
                    <Timeline.Connector>
                        <Timeline.Separator />
                        <Timeline.Indicator>
                            <Icon {...timelineIconProps} {...iconProps}>
                                {timelineIcon}
                            </Icon>
                        </Timeline.Indicator>
                    </Timeline.Connector>
                    <Timeline.Content>
                        <Timeline.Title>
                            <MSCollapsible
                                indicator={<Span {...timelineTextFormatting}><Span color="fg.muted">Stage </Span>{stageType}-{stageName}<Span color="fg.muted"> in Hook </Span>{hook} <Span color="fg.muted">from</Span> {source} {summary}</Span>}
                                defaultOpen={collapsibleOpen}
                                disableUntil="md"
                                timeline>
                                {error !== undefined && error !== null ? <ErrorAlert status={alertStatus} error={error}/> : null}
                            <Stack gap="2">
                                {diffs[index] !== null ? (
                                    <Fragment>
                                    <Heading size="sm">Diff</Heading>
                                {diffs[index]}</Fragment>
                            ) : null}
                                {inputs !== undefined && inputs.length > 0 ? (
                                    <Fragment>
                                        <Heading size="sm">Inputs</Heading>
                                        <Stack gap="1">
                                            {x.inputs.map((y, inputsIndex) => {
                                                return <ChakraCodeBlockShort key={`inputs-${inputsIndex}`} code={y.input} title={y.type} />
                                            })}
                                        </Stack></Fragment>) : null}
                            </Stack>
                            </MSCollapsible>
                        </Timeline.Title>
                    </Timeline.Content>
                </Timeline.Item>
            })}
            {finalPlay !== undefined ? (
                <Timeline.Item key="finalPlay" hideBelow="sm">
                    <Timeline.Connector>
                        <Timeline.Separator />
                        <Timeline.Indicator>
                            <Icon {...timelineIconProps}>
                                <MdMusicNote />
                            </Icon>
                        </Timeline.Indicator>
                    </Timeline.Connector>
                    <Timeline.Content>
                        <Timeline.Title {...timelineTextFormatting}>
                            Final Play<Span color="fg.muted">after all Transforms</Span>
                        </Timeline.Title>
                        <PlayData play={original} final={finalPlay} dates={false} compareDefault="Final" />
                    </Timeline.Content>
                </Timeline.Item>
            ) : null}
        </Timeline.Root>
    )
}