/**
 * AI-powered Bio Generation for Dating Profiles
 *
 * Uses OpenAI to generate natural, engaging dating profile bios based on
 * character attributes like personality, interests, occupation, and age.
 */

import OpenAI from 'openai';
import { config } from '../../config.js';

// Lazy initialization for OpenAI client
let openaiClient: OpenAI | null = null;

export interface BioPersonData {
  firstname?: string;
  ageYears?: number;
  age_years?: number;
  occupation?: string;
  likes?: string[];
  sex?: string;
}

export type LifeChangeEvent =
  | 'job_change'
  | 'graduation'
  | 'promotion'
  | 'breakup'
  | 'marriage'
  | 'major_achievement';

// Bio cache with timestamps
interface CachedBio {
  bio: string;
  generatedAt: Date;
}

const bioCache: Map<string, CachedBio> = new Map();
const BIO_CACHE_DURATION_DAYS = 30;

/**
 * Get or initialize the OpenAI client lazily.
 */
function getOpenAIClient(): OpenAI {
  if (!openaiClient) {
    const apiKey = config.OPENAI_API_KEY;
    if (!apiKey) {
      throw new Error('OPENAI_API_KEY not configured');
    }
    openaiClient = new OpenAI({ apiKey });
  }
  return openaiClient;
}

/**
 * Generate a dating profile bio using OpenAI.
 *
 * @param person - Person data with character attributes
 * @returns Generated bio string (2-3 sentences)
 */
export async function generateDatingBio(person: BioPersonData): Promise<string> {
  // Extract person attributes with defaults
  const firstname = person.firstname ?? 'Someone';
  const age = person.ageYears ?? person.age_years ?? 18;
  const occupation = person.occupation ?? 'Student';
  const likes = person.likes ?? [];
  const sex = person.sex ?? 'Unknown';

  // Format interests for the prompt
  const interestsStr = likes.length > 0
    ? likes.slice(0, 3).join(', ')
    : 'reading, music, outdoor activities';

  // Construct the prompt
  const prompt = `Write a natural, engaging dating profile bio (2-3 sentences) for:
    Name: ${firstname}, Age: ${age}, Sex: ${sex}
    Occupation: ${occupation}
    Interests: ${interestsStr}

    Make it conversational, authentic, and appealing. Write in first person.
    Do not include emojis. Keep it under 150 characters.`;

  try {
    const client = getOpenAIClient();

    const response = await client.chat.completions.create({
      model: 'gpt-4o-mini',
      messages: [
        {
          role: 'system',
          content: 'You are a creative writer helping people create authentic dating profiles.',
        },
        {
          role: 'user',
          content: prompt,
        },
      ],
      max_tokens: 100,
      temperature: 0.8,
    });

    let bio = response.choices[0]?.message?.content?.trim() ?? '';

    // Remove quotes if OpenAI wrapped the response
    if (bio.startsWith('"') && bio.endsWith('"')) {
      bio = bio.slice(1, -1);
    }
    if (bio.startsWith("'") && bio.endsWith("'")) {
      bio = bio.slice(1, -1);
    }

    return bio;
  } catch (error) {
    console.error('Error generating bio with OpenAI:', error);
    // Return a simple fallback bio
    return `I'm ${firstname}, a ${age}-year-old ${occupation.toLowerCase()} who enjoys ${interestsStr}. Looking to meet new people!`;
  }
}

/**
 * Get cached bio or generate new one if needed.
 *
 * Bio is cached for 30 days. Will regenerate if:
 * - No bio exists in cache
 * - Bio is older than 30 days
 * - forceRegenerate is true
 *
 * @param personId - Person's ID
 * @param person - Person data
 * @param forceRegenerate - Force regeneration even if bio exists
 * @returns Bio string
 */
export async function getOrGenerateBio(
  personId: string,
  person: BioPersonData,
  forceRegenerate = false
): Promise<string> {
  const cached = bioCache.get(personId);

  if (!forceRegenerate && cached) {
    const daysSinceGeneration =
      (Date.now() - cached.generatedAt.getTime()) / (1000 * 60 * 60 * 24);

    if (daysSinceGeneration < BIO_CACHE_DURATION_DAYS) {
      return cached.bio;
    }
  }

  // Generate new bio
  const bio = await generateDatingBio(person);

  // Cache the bio
  bioCache.set(personId, {
    bio,
    generatedAt: new Date(),
  });

  return bio;
}

/**
 * Regenerate bio when major life events occur.
 *
 * Triggers regeneration for events like:
 * - Job change
 * - Graduation
 * - Major achievement
 * - Relationship status change
 *
 * @param personId - Person's ID
 * @param person - Person data
 * @param eventType - Type of life event that occurred
 * @returns New bio if regenerated, null if event doesn't trigger regeneration
 */
export async function regenerateBioOnLifeChange(
  personId: string,
  person: BioPersonData,
  eventType: LifeChangeEvent | string
): Promise<string | null> {
  const regenerationTriggers: string[] = [
    'job_change',
    'graduation',
    'promotion',
    'breakup',
    'marriage',
    'major_achievement',
  ];

  if (regenerationTriggers.includes(eventType)) {
    return await getOrGenerateBio(personId, person, true);
  }

  return null;
}

/**
 * Generate bios for multiple people (with rate limiting).
 *
 * @param people - Array of objects with personId and person data
 * @param maxApiCalls - Maximum number of OpenAI API calls to make
 * @returns Map of personId to bio
 */
export async function batchGenerateBios(
  people: Array<{ personId: string; person: BioPersonData }>,
  maxApiCalls = 10
): Promise<Map<string, string | null>> {
  const results = new Map<string, string | null>();
  let apiCalls = 0;

  for (const { personId, person } of people) {
    if (apiCalls >= maxApiCalls) {
      console.log(`Reached max API calls limit (${maxApiCalls})`);
      break;
    }

    try {
      // Check if we already have a cached bio
      const cached = bioCache.get(personId);
      const needsGeneration = !cached ||
        (Date.now() - cached.generatedAt.getTime()) / (1000 * 60 * 60 * 24) >= BIO_CACHE_DURATION_DAYS;

      if (needsGeneration) {
        apiCalls++;
      }

      const bio = await getOrGenerateBio(personId, person);
      results.set(personId, bio);
    } catch (error) {
      console.error(`Error generating bio for person ${personId}:`, error);
      results.set(personId, null);
    }
  }

  return results;
}

/**
 * Clear bio cache for a specific person
 */
export function clearBioCache(personId: string): void {
  bioCache.delete(personId);
}

/**
 * Clear all bio caches (for testing)
 */
export function clearAllBioCaches(): void {
  bioCache.clear();
}

/**
 * Get bio cache statistics
 */
export function getBioCacheStats(): {
  totalCached: number;
  oldestCacheAge: number | null;
  newestCacheAge: number | null;
} {
  if (bioCache.size === 0) {
    return {
      totalCached: 0,
      oldestCacheAge: null,
      newestCacheAge: null,
    };
  }

  let oldestTime = Date.now();
  let newestTime = 0;

  for (const cached of bioCache.values()) {
    const time = cached.generatedAt.getTime();
    if (time < oldestTime) oldestTime = time;
    if (time > newestTime) newestTime = time;
  }

  const now = Date.now();
  return {
    totalCached: bioCache.size,
    oldestCacheAge: Math.floor((now - oldestTime) / (1000 * 60 * 60 * 24)),
    newestCacheAge: Math.floor((now - newestTime) / (1000 * 60 * 60 * 24)),
  };
}
