import RequestedBy from "@/components/ApprovalWorkflows/RequestedBy";
import StartedOn from "@/components/ApprovalWorkflows/StartedOn";
import Status from "@/components/ApprovalWorkflows/Status";
import Tip from "@/components/ApprovalWorkflows/Tip";
import UserWithProfilePic from "@/components/ApprovalWorkflows/UserWithProfilePic";
import WorkflowStepComponent from "@/components/ApprovalWorkflows/WorkflowStepComponent";
import Layout from "@/components/High/Layout";
import Button from "@/components/Low/Button";
import useApprovalWorkflow from "@/hooks/useApprovalWorkflow";
import { ApprovalWorkflow, getUserTypeLabelShort, WorkflowStep } from "@/interfaces/approval.workflow";
import { User } from "@/interfaces/user";
import { sessionOptions } from "@/lib/session";
import useExamStore from "@/stores/exam";
import { redirect, serialize } from "@/utils";
import { requestUser } from "@/utils/api";
import { getApprovalWorkflow } from "@/utils/approval.workflows.be";
import { getEntityWithRoles } from "@/utils/entities.be";
import { getExamById } from "@/utils/exams";
import { shouldRedirectHome } from "@/utils/navigation.disabled";
import { doesEntityAllow } from "@/utils/permissions";
import { getSpecificUsers, getUser } from "@/utils/users.be";
import axios from "axios";
import { AnimatePresence, LayoutGroup, motion } from "framer-motion";
import { withIronSessionSsr } from "iron-session/next";
import Head from "next/head";
import Link from "next/link";
import { useRouter } from "next/router";
import { useState } from "react";
import { BsChevronLeft } from "react-icons/bs";
import { FaSpinner, FaWpforms } from "react-icons/fa6";
import { FiSave } from "react-icons/fi";
import { IoMdCheckmarkCircleOutline } from "react-icons/io";
import { IoDocumentTextOutline } from "react-icons/io5";
import { MdKeyboardArrowDown, MdKeyboardArrowUp, MdOutlineDoubleArrow } from "react-icons/md";
import { RiThumbUpLine } from "react-icons/ri";
import { RxCrossCircled } from "react-icons/rx";
import { TiEdit } from "react-icons/ti";
import { toast, ToastContainer } from "react-toastify";

export const getServerSideProps = withIronSessionSsr(async ({ req, res, params }) => {
    const user = await requestUser(req, res);
    if (!user) return redirect("/login")

    if (shouldRedirectHome(user) || !["admin", "developer", "teacher", "corporate", "mastercorporate"].includes(user.type))
        return redirect("/")

    const { id } = params as { id: string };

    const workflow: ApprovalWorkflow | null = await getApprovalWorkflow("active-workflows", id);

    if (!workflow) return redirect("/approval-workflows")

    const entityWithRole = await getEntityWithRoles(workflow.entityId);
    if (!entityWithRole) return redirect("/approval-workflows");

    if (!doesEntityAllow(user, entityWithRole, "view_workflows")) return redirect("/approval-workflows");

    const allAssigneeIds: string[] = [
        ...new Set(
            workflow.steps
                .map(step => step.assignees)
                .flat()
        )
    ];

    return {
        props: serialize({
            user,
            initialWorkflow: workflow,
            id,
            workflowAssignees: await getSpecificUsers(allAssigneeIds),
            workflowRequester: await getUser(workflow.requester),
        }),
    };
}, sessionOptions);

interface Props {
    user: User,
    initialWorkflow: ApprovalWorkflow,
    id: string,
    workflowAssignees: User[],
    workflowRequester: User,
}

export default function Home({ user, initialWorkflow, id, workflowAssignees, workflowRequester }: Props) {

    const { workflow, reload, isLoading } = useApprovalWorkflow(id);

    const currentWorkflow = workflow || initialWorkflow;

    let currentStepIndex = currentWorkflow.steps.findIndex(step => !step.completed || step.rejected);
    if (currentStepIndex === -1)
        currentStepIndex = currentWorkflow.steps.length - 1;

    const [selectedStepIndex, setSelectedStepIndex] = useState<number>(currentStepIndex);
    const [selectedStep, setSelectedStep] = useState<WorkflowStep>(currentWorkflow.steps[selectedStepIndex]);
    const [isPanelOpen, setIsPanelOpen] = useState(true);
    const [isAccordionOpen, setIsAccordionOpen] = useState(false);
    const [comments, setComments] = useState<string>(selectedStep.comments || "");
    const [viewExamIsLoading, setViewExamIsLoading] = useState<boolean>(false);
    const [editExamIsLoading, setEditExamIsLoading] = useState<boolean>(false);

    const router = useRouter();

    const handleStepClick = (index: number, stepInfo: WorkflowStep) => {
        setSelectedStep(stepInfo);
        setSelectedStepIndex(index);
        setComments(stepInfo.comments || "");
        setIsPanelOpen(true);
    };

    const handleSaveComments = () => {
        const updatedWorkflow: ApprovalWorkflow = {
            ...currentWorkflow,
            steps: currentWorkflow.steps.map((step, index) =>
                index === selectedStepIndex ?
                    {
                        ...step,
                        comments: comments,
                    }
                    : step
            )
        };

        axios
            .put(`/api/approval-workflows/${id}`, updatedWorkflow)
            .then(() => {
                toast.success("Comments saved successfully.");
                reload();
            })
            .catch((reason) => {
                if (reason.response.status === 401) {
                    toast.error("Not logged in!");
                } else if (reason.response.status === 403) {
                    toast.error("You do not have permission to approve this step!");
                } else {
                    toast.error("Something went wrong, please try again later.");
                }
                console.log("Submitted Values:", updatedWorkflow);
                return;
            })
    };

    const handleApproveStep = () => {
        const isLastStep = (selectedStepIndex + 1 === currentWorkflow.steps.length);
        if (isLastStep) {
            if (!confirm(`Are you sure you want to approve the last step? Doing so will approve the exam.`)) return;
        }

        const updatedWorkflow: ApprovalWorkflow = {
            ...currentWorkflow,
            status: selectedStepIndex === currentWorkflow.steps.length - 1 ? "approved" : "pending",
            steps: currentWorkflow.steps.map((step, index) =>
                index === selectedStepIndex ?
                    {
                        ...step,
                        completed: true,
                        completedBy: user.id,
                        completedDate: Date.now(),
                    }
                    : step
            )
        };

        axios
            .put(`/api/approval-workflows/${id}`, updatedWorkflow)
            .then(() => {
                toast.success("Step approved successfully.");
                reload();
            })
            .catch((reason) => {
                if (reason.response.status === 401) {
                    toast.error("Not logged in!");
                } else if (reason.response.status === 403) {
                    toast.error("You do not have permission to approve this step!");
                } else {
                    toast.error("Something went wrong, please try again later.");
                }
                console.log("Submitted Values:", updatedWorkflow);
                return;
            })

        if (isLastStep) {
            setIsPanelOpen(false);
            const examModule = currentWorkflow.modules[0];
            const examId = currentWorkflow.examId;

            axios
                .patch(`/api/exam/${examModule}/${examId}`, { isDiagnostic: false })
                .then(() => toast.success(`The exam was successfuly approved and this workflow has been completed.`))
                .catch((reason) => {
                    if (reason.response.status === 404) {
                        toast.error("Exam not found!");
                        return;
                    }

                    if (reason.response.status === 403) {
                        toast.error("You do not have permission to update this exam!");
                        return;
                    }

                    toast.error("Something went wrong, please try again later.");
                })
                .finally(reload);
        } else {
            handleStepClick(selectedStepIndex + 1, currentWorkflow.steps[selectedStepIndex + 1]);
        }
    };

    const handleRejectStep = () => {
        if (!confirm(`Are you sure you want to reject this step? Doing so will terminate this approval workflow.`)) return;

        const updatedWorkflow: ApprovalWorkflow = {
            ...currentWorkflow,
            status: "rejected",
            steps: currentWorkflow.steps.map((step, index) =>
                index === selectedStepIndex ?
                    {
                        ...step,
                        completed: true,
                        completedBy: user.id,
                        completedDate: Date.now(),
                        rejected: true,
                    }
                    : step
            )
        };

        axios
            .put(`/api/approval-workflows/${id}`, updatedWorkflow)
            .then(() => {
                toast.success("Step rejected successfully.");
                reload();
            })
            .catch((reason) => {
                if (reason.response.status === 401) {
                    toast.error("Not logged in!");
                } else if (reason.response.status === 403) {
                    toast.error("You do not have permission to approve this step!");
                } else {
                    toast.error("Something went wrong, please try again later.");
                }
                console.log("Submitted Values:", updatedWorkflow);
                return;
            })
    };

    const dispatch = useExamStore((store) => store.dispatch);
    const handleViewExam = async () => {
        setViewExamIsLoading(true);
        const examModule = currentWorkflow.modules[0];
        const examId = currentWorkflow.examId;

        if (examModule && examId) {
            const exam = await getExamById(examModule, examId.trim());
            if (!exam) {
                toast.error(
                    "Unknown Exam ID! Please make sure you selected the right module and entered the right exam ID",
                    { toastId: "invalid-exam-id" }
                );
                setViewExamIsLoading(false);
                return;
            }
            dispatch({
                type: "INIT_EXAM",
                payload: { exams: [exam], modules: [examModule] },
            });
            router.push("/exam");
        }
    }

    const handleEditExam = () => {
        setEditExamIsLoading(true);
        const examModule = currentWorkflow.modules[0];
        const examId = currentWorkflow.examId;

        router.push(`/generation?id=${examId}&module=${examModule}`);
    }

    return (
        <>
            <Head>
                <title> Workflow | EnCoach</title>
                <meta
                    name="description"
                    content="A training platform for the IELTS exam provided by the Muscat Training Institute and developed by eCrop."
                />
                <meta name="viewport" content="width=device-width, initial-scale=1" />
                <link rel="icon" href="/favicon.ico" />
            </Head>
            <ToastContainer />

            <section className="flex items-center gap-2">
                <Link
                    href="/approval-workflows"
                    className="text-mti-purple hover:text-mti-purple-dark transition ease-in-out duration-300 text-xl">
                    <BsChevronLeft />
                </Link>
                <h1 className="text-2xl font-semibold">{currentWorkflow.name}</h1>
            </section>

            <section className="flex flex-col gap-6">
                <div className="flex flex-row gap-6">
                    <RequestedBy
                        prefix={getUserTypeLabelShort(workflowRequester.type)}
                        name={workflowRequester.name}
                        profileImage={workflowRequester.profilePicture}
                    />
                    <StartedOn
                        date={currentWorkflow.startDate}
                    />
                    <Status
                        status={currentWorkflow.status}
                    />
                </div>
                <div className="flex flex-row gap-3">
                    <Button
                        color="purple"
                        variant="solid"
                        onClick={handleViewExam}
                        disabled={viewExamIsLoading}
                        padding="px-6 py-2"
                        className="w-[240px] text-lg flex items-center justify-center gap-2 text-left"
                    >
                        {viewExamIsLoading ? (
                            <>
                                <FaSpinner className="animate-spin size-5" />
                                Loading...
                            </>
                        ) : (
                            <>
                                <IoDocumentTextOutline />
                                Load Exam
                            </>
                        )}
                    </Button>
                    <Button
                        color="purple"
                        variant="solid"
                        onClick={handleEditExam}
                        padding="px-6 py-2"
                        disabled={(!currentWorkflow.steps[currentStepIndex].assignees.includes(user.id) && user.type !== "admin" && user.type !== "developer") || editExamIsLoading}
                        className="w-[240px] text-lg flex items-center justify-center gap-2 text-left"
                    >
                        {editExamIsLoading ? (
                            <>
                                <FaSpinner className="animate-spin size-5" />
                                Loading...
                            </>
                        ) : (
                            <>
                                <TiEdit size={20} />
                                Edit Exam
                            </>
                        )}
                    </Button>

                </div>
                {currentWorkflow.steps.find((step) => !step.completed) === undefined &&
                    <Tip text="All steps in this instance have been completed." />
                }
            </section>

            <section className="flex flex-col gap-0">
                {currentWorkflow.steps.map((step, index) => (
                    <WorkflowStepComponent
                        workflowAssignees={workflowAssignees}
                        key={index}
                        completed={step.completed}
                        completedBy={step.completedBy}
                        rejected={step.rejected}
                        stepNumber={step.stepNumber}
                        stepType={step.stepType}
                        assignees={step.assignees}
                        finalStep={index === currentWorkflow.steps.length - 1}
                        currentStep={index === currentStepIndex}
                        selected={index === selectedStepIndex}
                        onClick={() => handleStepClick(index, step)}
                    />
                ))}
            </section>

            {/* Side panel */}
            <AnimatePresence mode="wait">
                <LayoutGroup key="sidePanel">
                    <section className={`absolute inset-y-0 right-0 h-full bg-mti-purple-ultralight bg-opacity-50 shadow-xl shadow-mti-purple transition-all duration-300 overflow-hidden ${isPanelOpen ? 'w-[500px]' : 'w-0'}`}>
                        {isPanelOpen && selectedStep && (
                            <motion.div
                                className="p-6"
                                key={selectedStep.stepNumber}
                                initial={{ opacity: 0, x: 30 }}
                                animate={{ opacity: 1, x: 0 }}
                                exit={{ opacity: 0, x: 30 }}
                                transition={{ duration: 0.2 }}
                            >
                                <hr className="my-4 h-[4px] bg-mti-purple-ultralight rounded-full w-full" />
                                <div className="flex flex-row gap-2">
                                    <p className="text-2xl font-medium text-left align-middle">Step {selectedStepIndex + 1}</p>
                                    <div className="ml-auto flex flex-row">
                                        <button
                                            className="min-w-fit max-h-fit text-lg font-medium flex items-center gap-2 text-left"
                                            onClick={() => setIsPanelOpen(false)}
                                        >
                                            Collapse
                                            <MdOutlineDoubleArrow size={20} />
                                        </button>
                                    </div>
                                </div>

                                <hr className="my-4 h-[4px] bg-mti-purple-ultralight rounded-full w-full" />

                                <div>
                                    <div className="my-8 flex flex-row gap-4 items-center text-lg font-medium">
                                        {selectedStep.stepType === "approval-by" ? (
                                            <>
                                                <RiThumbUpLine size={30} />
                                                Approval Step
                                            </>
                                        ) : (
                                            <>
                                                <FaWpforms size={30} />
                                                Form Intake Step
                                            </>
                                        )
                                        }
                                    </div>

                                    {selectedStep.completed ? (
                                        <div className={"text-base font-medium text-gray-500 flex flex-col gap-6"}>
                                            {selectedStep.rejected ? "Rejected" : "Approved"} on {new Date(selectedStep.completedDate!).toLocaleString("en-CA", {
                                                year: "numeric",
                                                month: "2-digit",
                                                day: "2-digit",
                                                hour: "2-digit",
                                                minute: "2-digit",
                                                second: "2-digit",
                                                hour12: false,
                                            }).replace(", ", " at ")}
                                            <div className="flex flex-row gap-1 text-sm">
                                                <p className="text-base">{selectedStep.rejected ? "Rejected" : "Approved"} by:</p>
                                                {(() => {
                                                    const assignee = workflowAssignees.find(
                                                        (assignee) => assignee.id === selectedStep.completedBy
                                                    );
                                                    return assignee ? (
                                                        <UserWithProfilePic
                                                            textSize="text-base"
                                                            prefix={getUserTypeLabelShort(assignee.type)}
                                                            name={assignee.name}
                                                            profileImage={assignee.profilePicture}
                                                        />
                                                    ) : (
                                                        "Unknown"
                                                    );
                                                })()}
                                            </div>
                                            <p className="text-sm">No additional actions are required.</p>
                                        </div>

                                    ) : (
                                        <div className={"text-base font-medium text-gray-500 mb-6"}>
                                            One assignee is required to sign off to complete this step:
                                            <div className="flex flex-col gap-2 mt-3">
                                                {workflowAssignees.filter(user => selectedStep.assignees.includes(user.id)).map(user => (
                                                    <span key={user.id}>
                                                        <UserWithProfilePic
                                                            textSize="text-sm"
                                                            prefix={`- ${getUserTypeLabelShort(user.type)}`}
                                                            name={user.name}
                                                            profileImage={user.profilePicture}
                                                        />
                                                    </span>
                                                ))}
                                            </div>
                                        </div>
                                    )}

                                    {selectedStepIndex === currentStepIndex && !selectedStep.completed && !selectedStep.rejected &&
                                        <div className="flex flex-row gap-2 ">
                                            <Button
                                                type="submit"
                                                color="purple"
                                                variant="solid"
                                                disabled={(!selectedStep.assignees.includes(user.id) && user.type !== "admin" && user.type !== "developer") || isLoading}
                                                onClick={handleApproveStep}
                                                padding="px-6 py-2"
                                                className="mb-3 w-full text-lg flex items-center justify-center gap-2 text-left"
                                            >
                                                {isLoading ? (
                                                    <>
                                                        <FaSpinner className="animate-spin size-5" />
                                                        Loading...
                                                    </>
                                                ) : (
                                                    <>
                                                        <IoMdCheckmarkCircleOutline size={20} />
                                                        Approve Step
                                                    </>
                                                )}
                                            </Button>
                                            <Button
                                                type="submit"
                                                color="red"
                                                variant="solid"
                                                disabled={(!selectedStep.assignees.includes(user.id) && user.type !== "admin" && user.type !== "developer") || isLoading}
                                                onClick={handleRejectStep}
                                                padding="px-6 py-2"
                                                className="mb-3 w-1/2 text-lg flex items-center justify-center gap-2 text-left"
                                            >
                                                {isLoading ? (
                                                    <>
                                                        <FaSpinner className="animate-spin size-5" />
                                                        Loading...
                                                    </>
                                                ) : (
                                                    <>
                                                        <RxCrossCircled size={20} />
                                                        Reject
                                                    </>
                                                )}
                                            </Button>
                                        </div>
                                    }

                                    <hr className="my-4 h-[4px] bg-mti-purple-ultralight rounded-full w-full" />

                                    {/* Accordion for Exam Changes */}
                                    <div className="mb-4">
                                        <div
                                            className="flex items-center justify-between cursor-pointer p-2 rounded-lg"
                                            onClick={() => setIsAccordionOpen((prev) => !prev)}
                                        >
                                            <h2 className="font-medium text-gray-500">
                                                Changes ({currentWorkflow.steps[selectedStepIndex].examChanges?.length || "0"})
                                            </h2>
                                            {isAccordionOpen ? (
                                                <MdKeyboardArrowUp size={24} />
                                            ) : (
                                                <MdKeyboardArrowDown size={24} />
                                            )}
                                        </div>
                                        <AnimatePresence>
                                            {isAccordionOpen && (
                                                <motion.div
                                                    initial={{ height: 0, opacity: 0 }}
                                                    animate={{ height: "auto", opacity: 1 }}
                                                    exit={{ height: 0, opacity: 0 }}
                                                    transition={{ duration: 0.3 }}
                                                    className="overflow-hidden mt-2"
                                                >
                                                    <div className="p-3 border border-gray-300 rounded-xl bg-white bg-opacity-80 overflow-y-auto max-h-40">
                                                        {currentWorkflow.steps[selectedStepIndex].examChanges?.length ? (
                                                            currentWorkflow.steps[selectedStepIndex].examChanges!.map((change, index) => (
                                                                <p key={index} className="text-sm text-gray-500 mb-2">
                                                                    {change}
                                                                </p>
                                                            ))
                                                        ) : (
                                                            <p className="text-normal text-opacity-70 text-gray-500">No changes made so far.</p>
                                                        )}
                                                    </div>
                                                </motion.div>
                                            )}
                                        </AnimatePresence>
                                    </div>

                                    <hr className="my-4 h-[4px] bg-mti-purple-ultralight rounded-full w-full" />

                                    <textarea
                                        value={comments}
                                        onChange={(e) => setComments(e.target.value)}
                                        placeholder="Input comments here"
                                        className="w-full h-40 p-2 border-2 rounded-xl shadow-lg focus:border-mti-purple focus:outline-none mt-3 resize-none"
                                    />

                                    <Button
                                        type="submit"
                                        color="purple"
                                        variant="solid"
                                        onClick={handleSaveComments}
                                        disabled={isLoading}
                                        padding="px-6 py-2"
                                        className="mt-6 mb-3 w-full text-lg flex items-center justify-center gap-2 text-left"
                                    >
                                        {isLoading ? (
                                            <>
                                                <FaSpinner className="animate-spin size-5" />
                                                Loading...
                                            </>
                                        ) : (
                                            <>
                                                <FiSave size={20} />
                                                Save Comments
                                            </>
                                        )}
                                    </Button>

                                    <hr className="my-4 h-[4px] bg-mti-purple-ultralight rounded-full w-full" />

                                </div>
                            </motion.div>
                        )}
                    </section>
                </LayoutGroup>
            </AnimatePresence>
        </>
    );
}
