import { type ReactNode } from 'react';

import { IconCheck, IconX } from '@material-hu/icons/tabler';

import { type CategorizedHourCategoryCount } from 'src/pages/dashboard/timeTracking/services';
import {
  type ApprovableStatus,
  type ApprovalRequest,
  type CategorizedHourApprovalStep,
  CategorizedHourApprovalStepState,
  type CategorizedHourStatusOverride,
  TimeTrackingCategorizedHourStatus,
} from 'src/types/timeTracking';
import { type LokaliseTFunction } from 'src/utils/i18n';
import { getFullName } from 'src/utils/userUtils';

import {
  type ApproverStepItem,
  type ApproverStepState,
} from 'src/components/dashboard/approvers/types';

type IsResolvingLastVisiblePendingRequestParams = {
  selectedList: 'pending' | 'processed';
  hasActiveFilters: boolean;
  requests: ApprovalRequest[];
  resolvingRequestId: string;
};

export const isResolvingLastVisiblePendingRequest = ({
  selectedList,
  hasActiveFilters,
  requests,
  resolvingRequestId,
}: IsResolvingLastVisiblePendingRequestParams) =>
  selectedList === 'pending' &&
  !hasActiveFilters &&
  requests.length === 1 &&
  requests[0]?.id === resolvingRequestId;

export const mapPotentialApproversForStep = (
  potentialApprovers: CategorizedHourApprovalStep['potentialApprovers'],
) => {
  if (potentialApprovers.length === 0) return undefined;
  return potentialApprovers;
};

const STEP_STATE_MAP: Record<
  CategorizedHourApprovalStepState,
  ApproverStepState
> = {
  [CategorizedHourApprovalStepState.PENDING]: 'pending',
  [CategorizedHourApprovalStepState.WAITING_OTHER_STEP]: 'waiting',
  [CategorizedHourApprovalStepState.APPROVED]: 'approved',
  [CategorizedHourApprovalStepState.REJECTED]: 'rejected',
};

export const mapToApproverStepItems = (
  steps: CategorizedHourApprovalStep[],
  t: LokaliseTFunction,
): ApproverStepItem[] =>
  steps.map(step => ({
    id: step.id,
    position: step.position,
    title: t(`general:step_positions.step_${step.position}`),
    state: STEP_STATE_MAP[step.state],
    approver: step.approver ?? undefined,
    potentialApprovers: mapPotentialApproversForStep(step.potentialApprovers),
    defaultExpanded: step.state === CategorizedHourApprovalStepState.PENDING,
  }));

export const getActionableStep = (
  steps: CategorizedHourApprovalStep[] | undefined,
) => steps?.find(s => s.state === CategorizedHourApprovalStepState.PENDING);

export const getStepApproverId = (
  step: CategorizedHourApprovalStep | undefined,
) => step?.approverId ?? step?.approver?.id ?? null;

export const getLastReviewedStep = (
  steps: CategorizedHourApprovalStep[] | undefined,
) =>
  steps
    ?.filter(
      step =>
        step.state === CategorizedHourApprovalStepState.APPROVED ||
        step.state === CategorizedHourApprovalStepState.REJECTED,
    )
    .sort((a, b) => b.position - a.position)[0];

/**
 * Step whose resolution the user is changing when editing from the drawer footer.
 * For a rejected request, prefer the highest-position rejected step so we do not
 * treat an earlier approved step as the "last" resolution (wrong flip action).
 */
export const getApprovalStepForResolutionEdit = (
  steps: CategorizedHourApprovalStep[] | undefined,
  categorizedHourStatus: TimeTrackingCategorizedHourStatus,
) => {
  if (steps == null || steps.length === 0) {
    return undefined;
  }
  if (categorizedHourStatus === TimeTrackingCategorizedHourStatus.REJECTED) {
    const rejectedDescending = [...steps]
      .filter(step => step.state === CategorizedHourApprovalStepState.REJECTED)
      .sort((a, b) => b.position - a.position);
    if (rejectedDescending.length > 0) {
      return rejectedDescending[0];
    }
  }
  return getLastReviewedStep(steps);
};

export type ApprovalStepCommentLine = {
  key: string;
  text: string;
  /** Rendered in bold before `text`. For step items this is the approver name; for the revoke item it's the admin name. */
  boldPrefix?: string;
  /** Present only on statusOverride-derived items. Drives the intro message rendered by ResponseReasonCard. */
  variant?: 'revoke' | 'change_to_approved';
};

/** Lines like "First Last: comment" in resolved step order (position ascending). */
export const getOrderedApprovalStepCommentLines = (
  steps: CategorizedHourApprovalStep[] | undefined,
  noCommentsText: string,
): ApprovalStepCommentLine[] => {
  if (steps == null || steps.length === 0) {
    return [];
  }

  return [...steps]
    .filter(
      step =>
        step.state !== CategorizedHourApprovalStepState.PENDING &&
        step.state !== CategorizedHourApprovalStepState.WAITING_OTHER_STEP,
    )
    .sort((a, b) => a.position - b.position)
    .map(step => {
      const commentText = step.comment?.trim() || noCommentsText;
      const approver = step.approver;
      if (approver == null) {
        return { key: step.position.toString(), text: commentText };
      }
      const approverName = approver.fullName?.trim() || getFullName(approver);
      return {
        key: step.position.toString(),
        boldPrefix: approverName || undefined,
        text: commentText,
      };
    });
};

const resolvedApprovalStepsHaveAnyNonEmptyComment = (
  steps: CategorizedHourApprovalStep[] | undefined,
) => {
  if (!steps || steps.length === 0) {
    return false;
  }
  return steps.some(
    step =>
      step.state !== CategorizedHourApprovalStepState.PENDING &&
      step.state !== CategorizedHourApprovalStepState.WAITING_OTHER_STEP &&
      Boolean(step.comment?.trim()),
  );
};

// TODO: note is for global approval/rejection comments, not for step edits
/**
 * Per-step read-only lines when at least one resolved step has a comment (empty
 * steps show `noCommentsText`); otherwise a single legacy `note`, or nothing.
 * When `statusOverride` is present, prepends an override line at the top.
 * - `overallStatus === REJECTED` → variant 'revoke' (request was revoked)
 * - `overallStatus === APPROVED` → variant 'change_to_approved' (revocation undone)
 */
export const getReadOnlyResponseCommentItems = (
  approvalSteps: CategorizedHourApprovalStep[] | undefined,
  response: string | null | undefined,
  noCommentsText: string,
  statusOverride?: CategorizedHourStatusOverride | null,
  overallStatus?: TimeTrackingCategorizedHourStatus,
): ApprovalStepCommentLine[] => {
  const overrideItems: ApprovalStepCommentLine[] = [];
  if (statusOverride != null) {
    const admin = statusOverride.admin;
    const adminName = admin ? getFullName(admin) || '' : '';
    const comment = statusOverride.comment?.trim() || '';
    if (adminName || comment) {
      const variant =
        overallStatus === TimeTrackingCategorizedHourStatus.APPROVED
          ? 'change_to_approved'
          : 'revoke';
      overrideItems.push({
        key: variant,
        variant,
        boldPrefix: adminName || undefined,
        text: comment,
      });
    }
  }

  if (resolvedApprovalStepsHaveAnyNonEmptyComment(approvalSteps)) {
    return [
      ...overrideItems,
      ...getOrderedApprovalStepCommentLines(approvalSteps, noCommentsText),
    ];
  }
  const trimmedComment = response?.trim();
  if (trimmedComment) {
    return [...overrideItems, { key: 'single-comment', text: trimmedComment }];
  }
  return overrideItems;
};

export const resolveStepId = (
  currentStepId: number | string | null | undefined,
): number | string | null => {
  if (currentStepId == null) {
    return null;
  }
  if (typeof currentStepId === 'number') {
    if (Number.isNaN(currentStepId)) {
      return null;
    }
    return currentStepId;
  }
  const trimmed = String(currentStepId).trim();
  if (trimmed === '') {
    return null;
  }
  return trimmed;
};

/** 0-based index of the active step for HuStepper `currentStep` (completed steps are before this index). */
export const getStepperActiveStep = (
  approverSteps: ApproverStepItem[] | undefined | null,
  status: TimeTrackingCategorizedHourStatus,
) => {
  if (approverSteps == null || approverSteps.length <= 1) {
    return 0;
  }
  const isResolvedApproved =
    status === TimeTrackingCategorizedHourStatus.APPROVED ||
    status === TimeTrackingCategorizedHourStatus.AUTO_APPROVED;
  if (isResolvedApproved) {
    return approverSteps.length;
  }

  const pendingIndex = approverSteps.findIndex(
    step => step.state === 'pending',
  );
  if (pendingIndex !== -1) {
    return pendingIndex;
  }

  const rejectedIndex = approverSteps.findIndex(
    step => step.state === 'rejected',
  );
  if (rejectedIndex !== -1) {
    return rejectedIndex;
  }

  const waitingIndex = approverSteps.findIndex(
    step => step.state === 'waiting',
  );
  if (waitingIndex !== -1) {
    return waitingIndex;
  }

  return approverSteps.length;
};

export type ProcessedListStatusPillDisplay = {
  labelKey: string;
  type: 'error' | 'success' | 'warning' | 'info' | 'neutral' | 'disabled';
};

/** Pill label (Lokalise key) and type for the processed-requests tab status column. */
export const getProcessedListStatusPillDisplay = (
  request: Pick<
    ApprovalRequest,
    'status' | 'isLoggedUserPotentialApprover' | 'isStatusOverridden'
  >,
): ProcessedListStatusPillDisplay => {
  if (request.status === TimeTrackingCategorizedHourStatus.REJECTED) {
    if (request.isStatusOverridden) {
      return { labelKey: 'approval_requests:revoked', type: 'disabled' };
    }
    return { labelKey: 'approval_requests:rejected', type: 'error' };
  }
  if (request.status === TimeTrackingCategorizedHourStatus.PENDING) {
    if (request.isLoggedUserPotentialApprover) {
      return {
        labelKey: 'general:pending',
        type: 'warning',
      };
    }
    return {
      labelKey: 'time_tracker:overtime_request_detail.status.in_process',
      type: 'info',
    };
  }
  return { labelKey: 'approval_requests:approved', type: 'success' };
};

export type EditAction = {
  label: string;
  icon: ReactNode;
  status: ApprovableStatus;
};

export type BulkReviewBreakdownItem = {
  hourCategoryId: number;
  hourCategoryName: string;
  count: number;
};

type GetBulkReviewBreakdownParams = {
  allRows: boolean;
  selectedIds: Set<string>;
  excludedIds: Set<string>;
  /** Cached rows from the infinite list — used to look up `hourCategoryName/Id` per id. */
  requests: ApprovalRequest[];
  /** From `/category-counts`; required only when `allRows` is true. */
  categoryCounts: CategorizedHourCategoryCount[] | undefined;
};

/**
 * Hour-category breakdown shown inside the bulk-review confirmation dialog.
 *
 * In `allRows` mode the BE's `/category-counts` endpoint does not accept
 * `excludeCategorizedHourIds`, so per-category subtraction is done client-side
 * from the cached rows. An excluded row that's not in the cache (selected,
 * scrolled out, cache evicted) is skipped — the breakdown is informational and
 * the bulk-review request still sends the exact `excludeCategorizedHourIds`.
 */
export const getBulkReviewBreakdown = ({
  allRows,
  selectedIds,
  excludedIds,
  requests,
  categoryCounts,
}: GetBulkReviewBreakdownParams): BulkReviewBreakdownItem[] => {
  if (!allRows) {
    const map = new Map<number, BulkReviewBreakdownItem>();
    requests.forEach(req => {
      if (!selectedIds.has(req.id)) return;
      const existing = map.get(req.hourCategoryId);
      if (existing) {
        existing.count += 1;
      } else {
        map.set(req.hourCategoryId, {
          hourCategoryId: req.hourCategoryId,
          hourCategoryName: req.hourCategoryName,
          count: 1,
        });
      }
    });
    return [...map.values()].filter(item => item.count > 0);
  }

  if (!categoryCounts) return [];

  const subtractByCategory = new Map<number, number>();
  if (excludedIds.size > 0) {
    requests.forEach(req => {
      if (!excludedIds.has(req.id)) return;
      subtractByCategory.set(
        req.hourCategoryId,
        (subtractByCategory.get(req.hourCategoryId) ?? 0) + 1,
      );
    });
  }

  return categoryCounts
    .map(c => ({
      hourCategoryId: c.hourCategoryId,
      hourCategoryName: c.hourCategoryName,
      count: Math.max(
        0,
        c.count - (subtractByCategory.get(c.hourCategoryId) ?? 0),
      ),
    }))
    .filter(item => item.count > 0);
};

export const checkEditAction = (
  isRevoked: boolean,
  stepForResolutionEditWasApproved: boolean,
): EditAction => {
  if (isRevoked) {
    return {
      label: 'approval_requests:change_to_approved',
      icon: <IconCheck size={16} />,
      status: TimeTrackingCategorizedHourStatus.APPROVED as ApprovableStatus,
    };
  }
  if (stepForResolutionEditWasApproved) {
    return {
      label: 'approval_requests:reject',
      icon: <IconX size={16} />,
      status: TimeTrackingCategorizedHourStatus.REJECTED as ApprovableStatus,
    };
  }
  return {
    label: 'approval_requests:approve',
    icon: <IconCheck size={16} />,
    status: TimeTrackingCategorizedHourStatus.APPROVED as ApprovableStatus,
  };
};
