import isNil from 'lodash-es/isNil';

import { type PillsProps } from '@material-hu/components/design-system/Pills';

import {
  COMMENT,
  DeliveryStatus,
  MenuItems,
  ReviewCommentableStatus,
  ReviewCycleStatus,
  type ReviewDetail,
  type ReviewDetailFormQuestions,
  type ReviewDetailFormQuestionsGoalItem,
  ReviewDirection,
  type ReviewUser,
} from 'src/pages/dashboard/performance/types';

import { PILL_TYPE, REVIEW_DIRECTION_ORDER } from './constants';

export type ReviewDirectionGroup<T> = {
  titleKey: string;
  reviews: T[];
};

const DIRECTION_ALIASES: Partial<Record<ReviewDirection, ReviewDirection[]>> = {
  [ReviewDirection.SUBORDINATE_REVIEW]: [ReviewDirection.CALIBRATION_REVIEW],
};

const belongsToDirection = (
  reviewType: ReviewDirection,
  direction: ReviewDirection,
) =>
  reviewType === direction ||
  (DIRECTION_ALIASES[direction]?.includes(reviewType) ?? false);

export const groupReviewsByDirection = <T extends { type: ReviewDirection }>(
  reviews: T[],
): ReviewDirectionGroup<T>[] => {
  const result: ReviewDirectionGroup<T>[] = [];

  REVIEW_DIRECTION_ORDER.forEach(({ direction, titleKey }) => {
    const filtered = reviews.filter(review =>
      belongsToDirection(review.type, direction),
    );
    if (filtered.length > 0) {
      result.push({ titleKey, reviews: filtered });
    }
  });

  return result;
};

export const getEffectiveReviewForm = (review: ReviewDetail | undefined) =>
  review?.calibrationForm ?? review?.form ?? null;

export const getIsReviewCompleted = (review: ReviewDetail): boolean => {
  const shared = [
    DeliveryStatus.SHARED_CONFIRMED,
    DeliveryStatus.SHARED_UNCONFIRMED,
  ].includes(review.shareStatus);
  return review.answered || shared;
};

export const getReviewDisplayStatus = (
  review: ReviewDetail,
): ReviewCycleStatus => {
  if (review.status === ReviewCycleStatus.EDIT_REQUESTED) {
    return ReviewCycleStatus.EDIT_REQUESTED;
  }

  if (review.status === ReviewCycleStatus.EDIT_PENDING) {
    return ReviewCycleStatus.EDIT_PENDING;
  }

  return getIsReviewCompleted(review)
    ? ReviewCycleStatus.FINISHED
    : ReviewCycleStatus.PENDING;
};

export const getReviewStatusPillType = (
  status: ReviewCycleStatus,
): PillsProps['type'] => {
  switch (status) {
    case ReviewCycleStatus.FINISHED:
      return PILL_TYPE.SUCCESS;
    case ReviewCycleStatus.EDIT_REQUESTED:
      return PILL_TYPE.WARNING;
    case ReviewCycleStatus.EDIT_PENDING:
      return PILL_TYPE.INFO;
    default:
      return PILL_TYPE.NEUTRAL;
  }
};

const parseAnswer = (answer: unknown) =>
  typeof answer === 'string' && Number(answer) >= 0 ? Number(answer) : answer;

/**
 * Generates an array of review answer objects from form values and question configurations.
 *
 * Iterates through the form values and, for each valid answer key (ignoring comment keys), locates the
 * corresponding question configuration. If a question requires a comment and none is provided, the answer
 * is used as the comment.
 *
 * @param {Object} values - Form values keyed by question IDs.
 * @param {Array<Object>} questions - Array of question configurations, each containing:
 *   - id {string} - Unique identifier for the question.
 *   - commentableStatus {string} - Indicates if a comment is required ("REQUIRED").
 * @returns {Array<Object>} Array of answer objects, each with:
 *   - questionId {string} - The question's ID.
 *   - answer {number|string} - The parsed answer.
 *   - comment {string|undefined} - The comment (either provided or, if required and missing, the answer).
 */
export const createReviewAnswers = (
  values: Record<string, unknown>,
  questions: ReviewDetailFormQuestions[] = [],
) =>
  Object.keys(values).reduce(
    (
      acc: Array<{ questionId: string; answer: unknown; comment?: unknown }>,
      elem,
    ) => {
      const answer = values[elem];

      const noAnswer =
        isNil(answer) ||
        (typeof answer === 'string' && answer.trim() === '') ||
        (Array.isArray(answer) && answer.length === 0);

      const emptyArray = Array.isArray(answer) && answer.length === 0;

      const excludeElem = elem.includes(COMMENT) || noAnswer || emptyArray;

      if (excludeElem) return acc;

      const question = questions.find(q => q.id === elem);
      const comment =
        question?.commentableStatus === ReviewCommentableStatus.REQUIRED &&
        isNil(values[`${elem}${COMMENT}`])
          ? String(answer)
          : values[`${elem}${COMMENT}`];

      acc.push({
        questionId: elem,
        answer: parseAnswer(answer),
        comment,
      });
      return acc;
    },
    [],
  );

export const needsAComment = (status: ReviewCommentableStatus) =>
  status !== ReviewCommentableStatus.NONE;

export const isCommentRequired = (status: ReviewCommentableStatus) =>
  status === ReviewCommentableStatus.REQUIRED;

export const getCompletionPercentage = (completed: number, total: number) =>
  total ? Math.floor(100 * (completed / total)) : 0;

export const isDownwardReview = (typeReview: ReviewDirection) =>
  typeReview === ReviewDirection.SUBORDINATE_REVIEW ||
  typeReview === ReviewDirection.CALIBRATION_REVIEW;

export const isSharedConfirmed = (
  isMyResults: boolean,
  shareStatus: DeliveryStatus,
) => isMyResults && shareStatus === DeliveryStatus.SHARED_CONFIRMED;

/**
 * Splits an array of review inputs into three question groups.
 *
 * - **formQuestions**: Common form questions (input.type has value, input.goal and input.competency do not).
 * - **competencyQuestions**: Questions where input.type and input.competency have value (input.goal does not).
 * - **goalQuestions**: Single array combining:
 *   - Common goal questions: input.type and input.goal have value (input.competency does not), with isAutomatic: false.
 *   - Automatic goal questions: input.type and input.competency do not have value, input.goal has value, with isAutomatic: true.
 *
 * @param reviewInputs - The array of review input objects.
 * @returns Object with formQuestions, goalQuestions (with isAutomatic), and competencyQuestions.
 */
export const splitReviewInputs = (
  reviewInputs: ReviewDetailFormQuestions[],
): {
  formQuestions: ReviewDetailFormQuestions[];
  goalQuestions: ReviewDetailFormQuestionsGoalItem[];
  competencyQuestions: ReviewDetailFormQuestions[];
} => {
  const formQuestions: ReviewDetailFormQuestions[] = [];
  const goalQuestions: ReviewDetailFormQuestionsGoalItem[] = [];
  const competencyQuestions: ReviewDetailFormQuestions[] = [];

  reviewInputs.forEach(input => {
    const hasType = !isNil(input.type);
    const hasGoal = !isNil(input.goal);
    const hasCompetency = !isNil(input.competency);

    if (hasType && !hasGoal && !hasCompetency) {
      formQuestions.push(input);
    } else if (hasType && hasCompetency && !hasGoal) {
      competencyQuestions.push(input);
    } else if (hasType && hasGoal && !hasCompetency) {
      goalQuestions.push({ ...input, isAutomatic: false });
    } else if (!hasType && !hasCompetency && hasGoal) {
      goalQuestions.push({ ...input, isAutomatic: true });
    }
  });

  return { formQuestions, goalQuestions, competencyQuestions };
};

export const buildReviewValues = (
  questions: ReviewDetailFormQuestions[],
): Record<string, unknown> =>
  questions.reduce<Record<string, unknown>>((acc, question) => {
    if (!question?.id) return acc;
    acc[question.id] = question.answer ?? undefined;
    acc[`${question.id}${COMMENT}`] = question.comment ?? '';
    return acc;
  }, {});

/**
 * Checks if a review cycle is finished based on status
 *
 * @param {ReviewCycleStatus} status - The review or cycle status to check
 * @returns {boolean} True if the status indicates the review/cycle is finished
 */
export const getIsFinished = (status: ReviewCycleStatus): boolean =>
  status === ReviewCycleStatus.FINISHED;

/**
 * Checks if a review has been shared (confirmed or unconfirmed)
 *
 * @param {DeliveryStatus} shareStatus - The share status to check
 * @returns {boolean} True if the review has been shared
 */
export const getIsReviewShared = (shareStatus: DeliveryStatus): boolean =>
  [DeliveryStatus.SHARED_CONFIRMED, DeliveryStatus.SHARED_UNCONFIRMED].includes(
    shareStatus,
  );

/**
 * Formats a score value for display by converting from backend integer format to decimal format.
 *
 * The backend stores scores as integers (e.g., 85 for 8.5), so we divide by 10 to convert
 * to the proper decimal representation for display purposes.
 *
 * @param score - The raw score value from the backend (integer format)
 * @param decimals - The number of decimal places to display (defaults to 1)
 * @returns A formatted string representation of the score with the specified decimal places
 *
 * @example
 * formatScoreToDisplay(85) // returns "8.5"
 * formatScoreToDisplay(92, 2) // returns "9.20"
 * formatScoreToDisplay(-73, 0) // returns "7" (absolute value is used)
 */

export const formatScoreToDisplay = (score: number, decimals: number = 1) => {
  const absoluteScore = Math.abs(score);
  const formattedScore = absoluteScore / 10;
  return formattedScore.toFixed(decimals);
};

/**
 * Returns the display string for score and/or scoreTag.
 *
 * @param score - The numeric score value (can be null/undefined)
 * @param scoreTag - The score tag label (can be null/undefined)
 * @returns Formatted string: "score | tag", "score", "tag", or undefined
 */
export const getScoreDisplay = (
  score: number | null | undefined,
  scoreTag: string | null | undefined,
) => {
  const formattedScore = !isNil(score) ? formatScoreToDisplay(score) : null;

  if (formattedScore && scoreTag) return `${formattedScore} | ${scoreTag}`;
  if (formattedScore) return formattedScore;
  return scoreTag;
};

const formatScore = (score: number | null | undefined): string => {
  if (isNil(score)) return '—';
  return score.toFixed(1);
};

/** Keys under `performance.cycles.status` in Lokalise / hu-translations. */
type CyclesStatusLeafKey =
  | 'draft'
  | 'finished'
  | 'in_progress'
  | 'pending'
  | 'edit_requested'
  | 'edit_pending'
  | 'selection';

/**
 * Maps API cycle status to existing `cycles.status.*` copy.
 * States without dedicated copy reuse `pending` or `in_progress` (see product note).
 */
const STATUS_TO_CYCLES_KEY: Record<ReviewCycleStatus, CyclesStatusLeafKey> = {
  [ReviewCycleStatus.FINISHED]: 'finished',
  [ReviewCycleStatus.IN_PROGRESS]: 'in_progress',
  [ReviewCycleStatus.PENDING]: 'pending',
  [ReviewCycleStatus.SELECTION]: 'selection',
  [ReviewCycleStatus.OMITTED]: 'pending',
  [ReviewCycleStatus.EDIT_REQUESTED]: 'edit_requested',
  [ReviewCycleStatus.EDIT_PENDING]: 'edit_pending',
};

export type CyclesStatusLabelKey = `cycles.status.${CyclesStatusLeafKey}`;

export function getCyclesStatusLabelKey(
  status: ReviewCycleStatus,
): CyclesStatusLabelKey {
  return `cycles.status.${STATUS_TO_CYCLES_KEY[status]}`;
}

export const formatPercentage = (
  numerator: number,
  denominator: number,
): string =>
  denominator > 0 ? `${((numerator / denominator) * 100).toFixed(1)}%` : '—';

export const formatRatio = (numerator: number, denominator: number): string =>
  denominator === 0 ? '—' : `${numerator} / ${denominator}`;

export const formatRowScore = (score: number | null | undefined): string =>
  isNil(score) ? '—' : formatScoreToDisplay(score);

export const formatTeamScoreDiff = (
  diff: number | null | undefined,
): string => {
  if (isNil(diff)) return '—';
  if (diff === 0) return '0.0';
  // API returns diff in tenths of a point (same encoding as score).
  const scaled = diff / 10;
  return `${scaled > 0 ? '+' : ''}${scaled.toFixed(1)}`;
};

export const getTeamScoreDiffColor = (
  diff: number | null | undefined,
): string => {
  if (isNil(diff) || diff === 0) return 'text.primary';
  if (diff > 0) return 'success.main';
  return 'error.main';
};

// User-level status per the leader dashboard contract is restricted to these 3.
// `ReviewUser.status` widens to ReviewCycleStatus today; narrow at the boundary.
type LeaderRowStatus =
  | ReviewCycleStatus.PENDING
  | ReviewCycleStatus.IN_PROGRESS
  | ReviewCycleStatus.FINISHED;

const isLeaderRowStatus = (
  status: ReviewCycleStatus,
): status is LeaderRowStatus =>
  status === ReviewCycleStatus.PENDING ||
  status === ReviewCycleStatus.IN_PROGRESS ||
  status === ReviewCycleStatus.FINISHED;

const ROW_STATUS_PILL_TYPES: Record<
  LeaderRowStatus,
  NonNullable<PillsProps['type']>
> = {
  PENDING: PILL_TYPE.NEUTRAL,
  IN_PROGRESS: PILL_TYPE.WARNING,
  FINISHED: PILL_TYPE.SUCCESS,
};

export const getRowStatusPillType = (status: ReviewCycleStatus) =>
  isLeaderRowStatus(status) ? ROW_STATUS_PILL_TYPES[status] : PILL_TYPE.NEUTRAL;

const ROW_STATUS_LABEL_KEYS: Record<LeaderRowStatus, string> = {
  PENDING: 'manage_team.status.pending',
  IN_PROGRESS: 'manage_team.status.in_progress',
  FINISHED: 'manage_team.row.status_finished',
};

export const getRowStatusLabelKey = (status: ReviewCycleStatus) =>
  isLeaderRowStatus(status)
    ? ROW_STATUS_LABEL_KEYS[status]
    : ROW_STATUS_LABEL_KEYS.PENDING;

export const getRowActionLabelKey = (user: ReviewUser): string => {
  if (user.totalReviews === 0) return 'cycles.item_button.select_reviewers';
  if (user.status !== ReviewCycleStatus.FINISHED)
    return 'manage_team.row.action_complete';
  if (user.shareStatus === DeliveryStatus.NOT_SHARED)
    return 'manage_team.row.action_share';
  return 'cycles.item_button.see_reviews';
};

/**
 * URL-friendly lowercase values used to serialize a `MenuItems` selection
 * as a query param (e.g. `/performance/:cycleId?menu=calibration` when
 * navigating back from a child route that needs to preselect a menu).
 */
export const MENU_ITEM_URL_VALUES: Record<MenuItems, string> = {
  [MenuItems.TEAM_PROGRESS]: 'team_progress',
  [MenuItems.COMPLETE_REVIEW]: 'complete_review',
  [MenuItems.CALIBRATION]: 'calibration',
  [MenuItems.MY_RESULTS]: 'my_results',
  [MenuItems.SHARE_RESULTS]: 'share_results',
};

/** Reverse lookup for `MENU_ITEM_URL_VALUES`. Returns `null` for unknown. */
export const parseMenuItemUrlValue = (
  value: string | null,
): MenuItems | null => {
  if (!value) return null;
  const entry = Object.entries(MENU_ITEM_URL_VALUES).find(
    ([, v]) => v === value,
  );
  return entry ? (entry[0] as MenuItems) : null;
};

export type ReviewerFormReviewApiItem = {
  id: number;
  answered: boolean;
  status: ReviewDetail['status'];
  shareStatus: ReviewDetail['shareStatus'];
  reviewed: Omit<
    ReviewDetail,
    'reviewId' | 'answered' | 'type' | 'status' | 'shareStatus'
  >;
  type: ReviewDetail['type'];
};

export const mapReviewerFormReviewsToReviewDetails = (
  reviewerFormReviews: ReviewerFormReviewApiItem[],
): ReviewDetail[] =>
  reviewerFormReviews.map(
    ({
      id: reviewId,
      answered,
      reviewed,
      type,
      status,
      shareStatus,
    }): ReviewDetail => ({
      reviewId,
      answered,
      type,
      ...reviewed,
      status,
      shareStatus,
    }),
  );
