/**
 * 用户画像打分（User Profile Scoring）V1.2
 *
 * 规则来源：
 * - `spec_kit/User Profile Scoring/spec.md`
 * - `设计说明文档/客戶端問卷打分規則.md`（V1.2）
 */

import type {
  BuildUserProfileOptions,
  ContextAnswer,
  EmotionAnswer,
  HardRules,
  MomStageAnswer,
  NeedAnswer,
  ProfileAnswered,
  QuestionnaireAnswersV1_2,
  SparseOneHot,
  UserProfileV1_2_Extended,
  UserStageOneHot,
} from './types';

const MS_PER_DAY = 24 * 60 * 60 * 1000;

function clamp(value: number, min: number, max: number): number {
  if (!Number.isFinite(value)) return min;
  return Math.min(max, Math.max(min, value));
}

function toDate(value: Date | string | undefined): Date | null {
  if (!value) return null;
  if (value instanceof Date) return Number.isFinite(value.getTime()) ? value : null;
  const d = new Date(value);
  return Number.isFinite(d.getTime()) ? d : null;
}

function isMomStageAnswer(v: unknown): v is MomStageAnswer {
  return v === 'expecting' || v === 'parenting' || v === 'unknown';
}

function isEmotionAnswer(v: unknown): v is EmotionAnswer {
  return (
    v === 'low' ||
    v === 'overwhelmed' ||
    v === 'tired' ||
    v === 'neutral' ||
    v === 'calm' ||
    v === 'joyful'
  );
}

function isContextAnswer(v: unknown): v is ContextAnswer {
  return v === 'family' || v === 'work' || v === 'relationship' || v === 'friends' || v === 'health';
}

function isNeedAnswer(v: unknown): v is NeedAnswer {
  return (
    v === 'emotional_support' ||
    v === 'parenting_pressure' ||
    v === 'self_worth' ||
    v === 'anxiety_relief' ||
    v === 'rest_balance'
  );
}

/**
 * 归一化答案：非法值按“跳过”处理（归一化为 undefined）
 * - `null` 保留，表示显式跳过/无值
 */
export function normalizeAnswers(raw: QuestionnaireAnswersV1_2): QuestionnaireAnswersV1_2 {
  const mom_stage =
    raw.mom_stage === null ? null : isMomStageAnswer(raw.mom_stage) ? raw.mom_stage : undefined;
  const emotion = raw.emotion === null ? null : isEmotionAnswer(raw.emotion) ? raw.emotion : undefined;
  const context = raw.context === null ? null : isContextAnswer(raw.context) ? raw.context : undefined;
  const need = raw.need === null ? null : isNeedAnswer(raw.need) ? raw.need : undefined;

  return { mom_stage, emotion, context, need };
}

export function computeProfileAnswered(normalized: QuestionnaireAnswersV1_2): ProfileAnswered {
  return {
    stage: normalized.mom_stage !== undefined && normalized.mom_stage !== null,
    emotion: normalized.emotion !== undefined && normalized.emotion !== null,
    context: normalized.context !== undefined && normalized.context !== null,
    need: normalized.need !== undefined && normalized.need !== null,
  };
}

/**
 * 时间衰减置信度（conf_time）
 * - 0–7 天：1.0
 * - 7–30 天：线性衰减到 0.7（含第 30 天）
 * - 30 天以上：0.5
 */
export function computeTimeConfidence(generatedAt: Date, now: Date): number {
  const deltaMs = now.getTime() - generatedAt.getTime();
  if (!Number.isFinite(deltaMs) || deltaMs <= 0) return 1.0;

  const days = deltaMs / MS_PER_DAY;
  if (days <= 7) return 1.0;
  if (days <= 30) {
    const t = (days - 7) / (30 - 7); // 0..1
    return 1.0 - 0.3 * t; // 1 -> 0.7
  }
  return 0.5;
}

/**
 * V1.2：profile_confidence（conf_U）
 * conf = clamp(conf_time * (0.5 + 0.5 * completion), 0.2, 1.0)
 */
export function computeProfileConfidence(confTime: number, answered: ProfileAnswered): number {
  const answeredCount =
    (answered.stage ? 1 : 0) + (answered.emotion ? 1 : 0) + (answered.context ? 1 : 0) + (answered.need ? 1 : 0);
  const completion = answeredCount / 4;
  const completionFactor = 0.5 + 0.5 * completion;
  return clamp(confTime * completionFactor, 0.2, 1.0);
}

function buildStageOneHot(momStage: MomStageAnswer | null | undefined): UserStageOneHot {
  // V1.2：mom_stage 跳过按安全策略输出 unknown=1
  if (momStage === null || momStage === undefined) {
    return { unknown: 1 };
  }

  return {
    expecting: momStage === 'expecting' ? 1 : 0,
    parenting: momStage === 'parenting' ? 1 : 0,
    unknown: momStage === 'unknown' ? 1 : 0,
  };
}

function mapEmotionScore(emotion: EmotionAnswer | null | undefined): number | null {
  if (emotion === null || emotion === undefined) return null;
  switch (emotion) {
    case 'low':
      return 0.0;
    case 'overwhelmed':
      return 0.2;
    case 'tired':
      return 0.4;
    case 'neutral':
      return 0.6;
    case 'calm':
      return 0.8;
    case 'joyful':
      return 1.0;
  }
}

function buildSparseOneHot(value: string | null | undefined): SparseOneHot {
  if (value === null || value === undefined) return {};
  return { [value]: 1 };
}

function computeRuleHitsAndHardRules(profile: {
  stage: UserStageOneHot;
  emotion_score: number | null;
}): { rule_hits: string[]; hard_rules: HardRules } {
  const rule_hits: string[] = [];
  const forbidden_risk_flags: string[] = [];

  const stageUnknown = profile.stage.unknown === 1;
  const stageParenting = profile.stage.parenting === 1;

  if (stageUnknown) {
    rule_hits.push('unsafe_for_stage_unknown');
    forbidden_risk_flags.push('unsafe_for_stage_unknown');
  }

  if (stageParenting) {
    rule_hits.push('unsafe_for_stage_parenting');
    forbidden_risk_flags.push('unsafe_for_stage_parenting');
  }

  if (profile.emotion_score !== null && profile.emotion_score <= 0.2) {
    rule_hits.push('unsafe_for_emotion_low');
    forbidden_risk_flags.push('unsafe_for_emotion_low');
  }

  const forbidden_content_predicates = [];
  if (stageUnknown) {
    forbidden_content_predicates.push({
      id: 'unknown_block_parenting_pressure_personalized',
      when_user: { stage_unknown: true },
      forbid_content: { need: 'parenting_pressure', personalization_power: 1 },
    });
  }

  return {
    rule_hits,
    hard_rules: {
      forbidden_risk_flags,
      forbidden_content_predicates,
    },
  };
}

export function buildUserProfileFromQuestionnaire(
  rawAnswers: QuestionnaireAnswersV1_2,
  options: BuildUserProfileOptions = {}
): UserProfileV1_2_Extended {
  const normalized = normalizeAnswers(rawAnswers);
  const profile_answered = computeProfileAnswered(normalized);

  const now = toDate(options.now) ?? new Date();
  const generatedAt = toDate(options.generatedAt) ?? now;

  const confTime = computeTimeConfidence(generatedAt, now);
  const profile_confidence = computeProfileConfidence(confTime, profile_answered);

  const stage = buildStageOneHot(normalized.mom_stage);
  const emotion_score = mapEmotionScore(normalized.emotion);
  const context = buildSparseOneHot(normalized.context);
  const need = buildSparseOneHot(normalized.need);

  const { rule_hits, hard_rules } = computeRuleHitsAndHardRules({ stage, emotion_score });

  return {
    profile_version: 'v1.2',
    profile_source: 'questionnaire',
    profile_generated_at: generatedAt.toISOString(),
    profile_confidence,
    profile_answered,
    stage,
    emotion_score,
    context,
    need,
    rule_hits,
    hard_rules,
  };
}

