/**
 * reliabilityScoring.ts
 *
 * 신뢰도 점수 계산 순수 함수 모듈 (Firebase 의존 없음 → 단위 테스트 용이)
 */

import {
  ReliabilityDimension,
  ReliabilityInput,
  ReliabilityOutput,
  ReliabilityWeightsConfig,
  FreshnessInterval,
} from './types/reliability';

// ── 설정 로드 (모듈 레벨 캐시) ──────────────────────────────────────────────

let _cachedConfig: ReliabilityWeightsConfig | null = null;

export function loadWeightsConfig(): ReliabilityWeightsConfig {
  if (_cachedConfig) {
    return _cachedConfig;
  }
  // eslint-disable-next-line @typescript-eslint/no-var-requires
  _cachedConfig = require('./config/reliabilityWeights.json') as ReliabilityWeightsConfig;
  return _cachedConfig;
}

// ── 1. freshness 점수 ────────────────────────────────────────────────────────

export function calculateFreshnessScore(
  updatedAt: Date,
  now: Date,
  intervals: FreshnessInterval[]
): number {
  const ageMs = now.getTime() - updatedAt.getTime();
  const ageDays = ageMs / (1000 * 60 * 60 * 24);

  for (const interval of intervals) {
    if (interval.maxDays === null) {
      return interval.score;
    }
    if (ageDays <= interval.maxDays) {
      return interval.score;
    }
  }

  // 구간 목록이 비어있는 경우 기본값
  return 0.1;
}

// ── 2. verification 점수 ─────────────────────────────────────────────────────

export function calculateVerificationScore(
  verificationStatus?: string,
  documentStatus?: string
): number {
  let base: number;

  switch (verificationStatus) {
    case 'expert_verified':
      base = 1.0;
      break;
    case 'auto_passed':
      base = 0.6;
      break;
    default:
      // 'unverified' 또는 없음
      base = 0.2;
      break;
  }

  if (documentStatus === 'approved') {
    base = Math.min(base + 0.1, 1.0);
  }

  return base;
}

// ── 3. authority 점수 ────────────────────────────────────────────────────────

const AUTHORITY_TIER_MAP: Record<number, number> = {
  1: 1.0,
  1.5: 0.9,
  2: 0.7,
  3: 0.5,
  3.5: 0.3,
  4: 0.2,
};

export function calculateAuthorityScore(authorityTier?: number): number {
  if (authorityTier === undefined || authorityTier === null) {
    return 0.1;
  }

  if (AUTHORITY_TIER_MAP[authorityTier] !== undefined) {
    return AUTHORITY_TIER_MAP[authorityTier];
  }

  // 매핑에 없는 값: 알려진 티어 간 선형 보간
  const knownTiers = Object.keys(AUTHORITY_TIER_MAP)
    .map(Number)
    .sort((a, b) => a - b);

  for (let i = 0; i < knownTiers.length - 1; i++) {
    const t1 = knownTiers[i];
    const t2 = knownTiers[i + 1];
    if (authorityTier > t1 && authorityTier < t2) {
      const ratio = (authorityTier - t1) / (t2 - t1);
      const s1 = AUTHORITY_TIER_MAP[t1];
      const s2 = AUTHORITY_TIER_MAP[t2];
      return s1 + (s2 - s1) * ratio;
    }
  }

  return 0.1;
}

// ── 4. source 점수 ───────────────────────────────────────────────────────────

const SOURCE_TYPE_MAP: Record<string, number> = {
  regulation: 1.0,
  policy_pdf: 0.9,
  court_ruling: 0.8,
  newsletter: 0.7,
  wiki_editorial: 0.6,
  kakao_expert: 0.5,
  youtube: 0.3,
  kakao_community: 0.3,
  user_submitted: 0.3,
};

export function calculateSourceScore(sourceType?: string): number {
  if (!sourceType) {
    return 0.2;
  }
  return SOURCE_TYPE_MAP[sourceType] ?? 0.2;
}

// ── 5. review 점수 ───────────────────────────────────────────────────────────

export function calculateReviewScore(reviewCount: number, hasApproval: boolean): number {
  if (reviewCount === 0) {
    return 0.0;
  }

  if (hasApproval) {
    return Math.min(0.8 + reviewCount * 0.1, 1.0);
  }

  // 승인 없이 리뷰만 있는 경우
  return Math.min(reviewCount * 0.2, 0.5);
}

// ── 6. sourceRef 점수 ────────────────────────────────────────────────────────

export function calculateSourceRefScore(hasSourceRef: boolean, sourceRefQuality?: string): number {
  if (!hasSourceRef) {
    return 0.0;
  }

  switch (sourceRefQuality) {
    case 'high':
      return 1.0;
    case 'medium':
      return 0.7;
    default:
      // 'low' 또는 없음
      return 0.4;
  }
}

// ── 7. knockout 적용 ─────────────────────────────────────────────────────────

export function applyKnockout(
  dimensions: Record<string, number>,
  config: { enabled: boolean; floorScore: number }
): { knockedOut: boolean; score: number } {
  const values = Object.values(dimensions);

  if (!config.enabled) {
    // knockout 비활성화: 단순 평균 반환
    const avg = values.reduce((sum, v) => sum + v, 0) / values.length;
    return { knockedOut: false, score: avg };
  }

  const hasZeroDimension = values.some((v) => v === 0);
  if (hasZeroDimension) {
    return { knockedOut: true, score: config.floorScore };
  }

  const avg = values.reduce((sum, v) => sum + v, 0) / values.length;
  return { knockedOut: false, score: avg };
}

// ── 8. 종합 신뢰도 점수 계산 ─────────────────────────────────────────────────

export function computeCompositeReliabilityScore(input: ReliabilityInput): ReliabilityOutput {
  const config = loadWeightsConfig();
  const now = input.now ?? new Date();

  const dimensions: Record<ReliabilityDimension, number> = {
    verification: calculateVerificationScore(input.verificationStatus, input.documentStatus),
    authority: calculateAuthorityScore(input.authorityTier),
    source: calculateSourceScore(input.sourceType),
    review: calculateReviewScore(input.reviewCount, input.hasApproval),
    freshness: calculateFreshnessScore(input.updatedAt, now, config.freshnessIntervals),
    sourceRef: calculateSourceRefScore(input.hasSourceRef, input.sourceRefQuality),
  };

  // knockout 체크: 0인 차원이 있으면 floorScore 반환
  const hasZeroDimension = Object.values(dimensions).some((v) => v === 0);

  if (config.knockout.enabled && hasZeroDimension) {
    return {
      compositeScore: config.knockout.floorScore,
      dimensions,
      knockedOut: true,
    };
  }

  // 가중 평균 계산
  const compositeScore = (Object.keys(dimensions) as ReliabilityDimension[]).reduce(
    (sum, dim) => sum + dimensions[dim] * config.weights[dim],
    0
  );

  return {
    compositeScore,
    dimensions,
    knockedOut: false,
  };
}
