import type { Logger } from "pino";
import { SQUAD_TRIBE_MAP, TRIBE_LABEL_CANDIDATES } from "@/core/constants.js";
import type { GitHubClientImpl } from "@/github/client.js";
import type { IssueFlow, RepoConfig } from "@/types/index.js";

type LabelMap = Map<string, string>;

function validateAgainstRepo(labels: string[], lowerToActual: LabelMap): string[] {
  return labels
    .map((l) => lowerToActual.get(l.toLowerCase()))
    .filter((l): l is string => l !== undefined);
}

function getFlowLabelsFromConfig(repo: RepoConfig, flow: IssueFlow | null): string[] {
  if (!repo.prLabels || flow === null) return [];
  if (flow === "hotfix") return repo.prLabels.hotfix ?? repo.prLabels.bugfix ?? [];
  if (flow === "bugfix") return repo.prLabels.bugfix ?? [];
  return repo.prLabels.default ?? [];
}

function resolveFlowLabels(
  repo: RepoConfig,
  flow: IssueFlow | null,
  lowerToActual: LabelMap | null,
  logger: Logger,
): string[] {
  const configLabels = getFlowLabelsFromConfig(repo, flow);
  if (configLabels.length === 0) return [];

  logger.info({ flow, flowLabels: configLabels }, "flow_labels_resolved");

  if (!lowerToActual) return [...configLabels];

  const result: string[] = [];
  for (const label of configLabels) {
    const resolved = lowerToActual.get(label.toLowerCase());
    if (resolved) {
      result.push(resolved);
    } else {
      logger.warn({ flow, label }, "flow_label_not_in_repo");
    }
  }
  return result;
}

function resolveTribeLabel(
  issueKey: string,
  lowerToActual: LabelMap | null,
  logger: Logger,
): string[] {
  if (!lowerToActual) return [];

  const prefix = issueKey.match(/^([A-Z]{2,10})-/)?.[1] ?? "";
  const tribe = SQUAD_TRIBE_MAP[prefix] ?? "";
  const candidates: string[] = tribe ? (TRIBE_LABEL_CANDIDATES[tribe] ?? []) : [];

  if (candidates.length === 0) return [];

  const tribeLabel = validateAgainstRepo(candidates, lowerToActual)[0];
  if (tribeLabel) {
    logger.info({ tribe, tribeLabel }, "tribe_label_resolved");
    return [tribeLabel];
  }

  logger.info({ tribe, candidates }, "tribe_label_not_found_in_repo");
  return [];
}

/**
 * Resolve PR labels by merging flow labels (from repos.json config) and
 * tribe labels.
 *
 * When repo labels can be fetched from GitHub, every label is validated
 * against them (case-insensitive) and resolved to the canonical repo name.
 * When the fetch fails (returns []):
 *  - Flow labels are added without validation.
 *  - Tribe labels are skipped (candidates are generic, higher risk of mismatch).
 */
export async function resolveLabels(
  githubClient: GitHubClientImpl,
  repo: RepoConfig,
  flow: IssueFlow | null,
  issueKey: string,
  logger: Logger,
): Promise<string[]> {
  const repoLabels = await githubClient.listRepoLabels();
  const lowerToActual =
    repoLabels.length > 0 ? new Map(repoLabels.map((l) => [l.toLowerCase(), l])) : null;

  return [
    ...resolveFlowLabels(repo, flow, lowerToActual, logger),
    ...resolveTribeLabel(issueKey, lowerToActual, logger),
  ];
}
