import { Icon } from '@iconify/react';
import Steps from '@/components/steps/Steps';
import StepItem from '@/components/steps/StepItem';
import Tooltip from '@/components/Tooltip';

import { cn, formatDate } from '@/lib/helper';
import {
  BaseApiResponse,
  BaseApproval,
  BaseGroupedApproval,
} from '@/types/api/api-general';
import { ApprovalLine } from '@/types/config/constant';
import useSWR from 'swr';
import { httpClientFetcher } from '@/services/http/client';
import { isResponseSuccess } from '@/lib/api-helper';
import { useCallback, useMemo } from 'react';

export type ApprovalStepStatus = 'APPROVED' | 'REJECTED' | 'WAITING' | 'IDLE';

export type ApprovalStepLog = {
  action: string;
  action_by?: string;
  date?: string;
  notes?: string | null;
};

interface ApprovalStepsProps {
  approvals: {
    name?: string;
    status: ApprovalStepStatus;
    logs?: ApprovalStepLog[];
  }[];
}

const ApprovalSteps = ({ approvals }: ApprovalStepsProps) => {
  return (
    <Steps direction='vertical' className='w-full md:steps-horizontal'>
      {approvals.map((approval, idx) => {
        const stepItemColor =
          approval.status === 'APPROVED'
            ? 'success'
            : approval.status === 'REJECTED'
              ? 'error'
              : approval.status === 'WAITING'
                ? 'warning'
                : undefined;

        const stepItemIcon =
          approval.status === 'APPROVED'
            ? 'material-symbols:check-rounded'
            : approval.status === 'REJECTED'
              ? 'material-symbols:close-rounded'
              : approval.status === 'WAITING'
                ? 'pajamas:dash-circle'
                : approval.logs && approval.logs.length > 0
                  ? 'material-symbols:info-outline-rounded'
                  : 'bxs:hourglass';

        return (
          <StepItem
            key={idx}
            color={stepItemColor}
            icon={
              <Tooltip
                color={stepItemColor}
                position='right'
                className={{
                  wrapper: 'md:tooltip-bottom',
                  content: 'p-0 rounded overflow-hidden',
                }}
                content={
                  <>
                    {approval.logs && approval.logs.length > 0 && (
                      <div className='flex flex-col gap-0'>
                        {approval.logs?.map((approvalLog, logIdx) => {
                          const action =
                            approvalLog.action === 'CREATED'
                              ? 'Dibuat'
                              : approvalLog.action === 'UPDATED'
                                ? 'Diperbarui'
                                : approvalLog.action === 'APPROVED'
                                  ? 'Disetujui'
                                  : approvalLog.action === 'REJECTED'
                                    ? 'Ditolak'
                                    : '-';

                          return (
                            <div
                              key={logIdx}
                              className={cn(
                                'p-2 flex flex-col text-base text-start',
                                {
                                  'bg-success text-success-content':
                                    approvalLog.action === 'APPROVED',
                                  'bg-error text-error-content':
                                    approvalLog.action === 'REJECTED',
                                  'bg-info text-info-content':
                                    approvalLog.action === 'CREATED',
                                  'bg-warning text-warning-content':
                                    approvalLog.action === 'UPDATED',
                                }
                              )}
                            >
                              {approvalLog.date && (
                                <span>
                                  {formatDate(
                                    approvalLog.date,
                                    'YYYY-MM-DD, HH:mm:ss'
                                  )}
                                </span>
                              )}
                              <span>Aksi: {action}</span>
                              <span>Oleh: {approvalLog.action_by ?? '-'}</span>
                              <span>Catatan: {approvalLog.notes ?? '-'}</span>
                            </div>
                          );
                        })}
                      </div>
                    )}
                  </>
                }
              >
                <Icon
                  icon={stepItemIcon}
                  width={24}
                  height={24}
                  className={cn({
                    invisible:
                      approval.status === 'IDLE' &&
                      (!approval.logs ||
                        (approval.logs && approval.logs.length === 0)),
                  })}
                />
              </Tooltip>
            }
          >
            {approval.name}
          </StepItem>
        );
      })}
    </Steps>
  );
};

export const formatGroupedApprovalsToApprovalSteps = (
  approvalLine: ApprovalLine,
  groupedApprovals: BaseGroupedApproval[],
  latestApproval: BaseApproval
): ApprovalStepsProps['approvals'] => {
  const formattedApprovalSteps: ApprovalStepsProps['approvals'] =
    approvalLine.map((approvalLineItem) => {
      const approvalGroup = groupedApprovals.find(
        (approvalGroupItem) =>
          approvalGroupItem.step_number === approvalLineItem.step_number
      );

      const currentStepNumber = approvalLineItem.step_number;
      const lastStepNumber =
        groupedApprovals[groupedApprovals.length - 1]?.step_number;

      const isLatestApprovalRejected = latestApproval.action === 'REJECTED';

      if (!approvalGroup && currentStepNumber <= lastStepNumber) {
        throw new Error(
          `Approval dengan ${approvalLineItem.step_name} tidak ditemukan!`
        );
      }

      if (!approvalGroup) {
        const isWaiting = currentStepNumber === latestApproval.step_number + 1;
        const isPreviousApprovalRejected =
          groupedApprovals[groupedApprovals.length - 1].approvals[0].action ===
          'REJECTED';

        return {
          name: approvalLineItem.step_name,
          status: isPreviousApprovalRejected
            ? 'IDLE'
            : isWaiting
              ? 'WAITING'
              : 'IDLE',
        };
      }

      let approvalStatus: ApprovalStepStatus = 'IDLE';

      if (approvalGroup.step_number <= latestApproval.step_number) {
        if (approvalGroup.approvals) {
          switch (approvalGroup?.approvals[0]?.action) {
            case 'CREATED':
            case 'UPDATED':
            case 'APPROVED':
              approvalStatus = 'APPROVED';
              break;

            case 'REJECTED':
              approvalStatus = 'REJECTED';
              break;

            default:
              approvalStatus = 'IDLE';
              break;
          }
        }
      } else if (
        approvalGroup.step_number === latestApproval.step_number + 1 &&
        !isLatestApprovalRejected
      ) {
        approvalStatus = 'WAITING';
      } else {
        approvalStatus = 'IDLE';
      }

      const approvalLogs: ApprovalStepLog[] = approvalGroup.approvals
        ? approvalGroup.approvals.map((approval) => ({
            action_by: approval.action_by.name,
            date: approval.action_at,
            notes: approval.notes,
            action: approval.action,
          }))
        : [];

      return {
        name: approvalGroup.step_name,
        status: approvalStatus,
        logs: approvalLogs,
      };
    });

  return formattedApprovalSteps;
};

export default ApprovalSteps;

/**
 * Mengubah array BaseApproval (datar) menjadi BaseGroupedApproval (berkelompok).
 */
const groupApprovalsByStep = (
  approvals: BaseApproval[]
): BaseGroupedApproval[] => {
  const groups: Record<number, BaseGroupedApproval> = {};
  for (const approval of approvals) {
    if (!groups[approval.step_number]) {
      groups[approval.step_number] = {
        step_number: approval.step_number,
        step_name: approval.step_name,
        approvals: [],
      };
    }
    groups[approval.step_number].approvals.push(approval);
  }
  return Object.values(groups);
};

/**
 * Mengubah array BaseGroupedApproval (berkelompok) kembali menjadi BaseApproval[] (datar).
 */
const flattenGroupedApprovals = (
  groupedApprovals: BaseGroupedApproval[]
): BaseApproval[] => {
  return groupedApprovals.flatMap((group) => group.approvals);
};

/**
 * Type guard untuk memeriksa apakah data adalah BaseGroupedApproval[].
 */
const isGroupedApprovalData = (
  data: BaseApproval[] | BaseGroupedApproval[]
): data is BaseGroupedApproval[] => {
  if (!data || data.length === 0) {
    return true;
  }
  const firstElement = data[0];
  return (
    typeof firstElement === 'object' &&
    firstElement !== null &&
    'approvals' in firstElement &&
    Array.isArray(firstElement.approvals)
  );
};

const useApprovalSteps = ({
  latestApproval,
  approvalLines,
  moduleName,
  moduleId,
  params,
}: {
  latestApproval: BaseApproval | undefined;
  approvalLines: ApprovalLine;
  moduleName: string;
  moduleId: string;
  params?: {
    page?: number;
    limit: number;
    search?: string;
    group_step_number?: boolean;
  };
}) => {
  // Membuat URL Parameters
  const paramString = new URLSearchParams({
    page: params?.page?.toString() || '',
    limit: params?.limit?.toString() || '',
    search: params?.search || '',
  }).toString();

  // fetching data approvals
  const SWR_KEY_APPROVALS =
    moduleName && moduleId
      ? `/approvals?module_name=${moduleName}&module_id=${moduleId}${
          params ? `&${paramString}` : ''
        }`
      : null;

  const {
    data: approvalData,
    isLoading: approvalIsLoading,
    mutate: mutateApprovals,
  } = useSWR(SWR_KEY_APPROVALS, async (url) => {
    return await httpClientFetcher<
      BaseApiResponse<BaseApproval[] | BaseGroupedApproval[]>
    >(url);
  });

  // Fungsi Refresh
  const refresh = useCallback(async () => {
    await mutateApprovals();
  }, [mutateApprovals]);

  const { groupedApprovals } = useMemo(() => {
    const rawData = isResponseSuccess(approvalData)
      ? approvalData.data
      : undefined;

    let processedGroupedApprovals: BaseGroupedApproval[] = [];

    if (rawData) {
      if (isGroupedApprovalData(rawData)) {
        processedGroupedApprovals = rawData;
      } else {
        processedGroupedApprovals = groupApprovalsByStep(
          rawData as BaseApproval[]
        );
      }
    }

    return {
      groupedApprovals: processedGroupedApprovals,
    };
  }, [approvalData]);

  const isLoading = approvalIsLoading;

  // Formatting Akhir
  const approvals = useMemo(() => {
    if (isLoading || !approvalLines.length || !latestApproval) {
      return [];
    }
    try {
      return formatGroupedApprovalsToApprovalSteps(
        approvalLines,
        groupedApprovals,
        latestApproval
      );
    } catch (error) {
      console.warn('Gagal memformat approval steps:', error);
      return [];
    }
  }, [isLoading, approvalLines, groupedApprovals, latestApproval]);

  // Raw Data Approvals
  const rawDataApprovals = useMemo(() => {
    const rawData = isResponseSuccess(approvalData)
      ? approvalData.data
      : undefined;

    if (!rawData) {
      return undefined;
    }

    const isDataCurrentlyGrouped = isGroupedApprovalData(rawData);
    const wantsGrouped = params?.group_step_number !== false;

    if (wantsGrouped) {
      if (isDataCurrentlyGrouped) {
        return rawData as BaseGroupedApproval[];
      } else {
        return groupApprovalsByStep(rawData as BaseApproval[]);
      }
    } else {
      if (isDataCurrentlyGrouped) {
        return flattenGroupedApprovals(rawData as BaseGroupedApproval[]);
      } else {
        return rawData as BaseApproval[];
      }
    }
  }, [approvalData, params?.group_step_number]);

  // Return Hook
  return {
    approvals,
    isLoading,
    rawDataApprovals: rawDataApprovals,
    refresh,
  };
};

export { useApprovalSteps };
