import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import axios from 'axios';
import OpenAI from 'openai';
import { Repository } from 'typeorm';
import { Constants } from 'src/common/constants';
import { KeyType, Settings } from 'src/entities/settings.entity';
import { EncryptionService } from '../encryption/encryption.service';

@Injectable()
export class OpenAIService {
  // Store in .env file

  constructor(
    private readonly encryptionService: EncryptionService,
    @InjectRepository(Settings)
    private readonly Settings: Repository<Settings>,
  ) { }
  async generateResponse(prompt: string): Promise<string> {
    const apiKey = await this.encryptionService.decrypt(process.env.OPENAI_API_KEY);
    const apiUrl = process.env.OPENAI_API_URL;
    try {
      const response = await axios.post(apiUrl,
        {
          model: 'gpt-4.1-mini', // Change to gpt-3.5-turbo if needed
          messages: [{ role: 'user', content: prompt }],
        },
        {
          headers: {
            'Authorization': `Bearer ${apiKey}`,
            'Content-Type': 'application/json',
          },
        },
      );

      return response.data.choices[0].message.content;
    } catch (error: any) {
      console.error('Error calling OpenAI API:', error);
      throw new HttpException(error.message, HttpStatus.BAD_REQUEST);
    }
  }

  async createCampaignPrompt(request: any) {

    const { campaign_name, budget_per_influencer, number_of_influencers, content_type, influencer_type, job_requirement, posting_start_date, posting_end_date, first_draft_date, profile_privacy, campaign_requirements, attachment, currency, platforms, categories, is_barter } = request;

    let prompt = Constants.AI_PROMPT.CAMPAIGN_BRIEFING;

    prompt += ` The campaign name is "${campaign_name}".`;

    if (budget_per_influencer && currency) {
      prompt += ` The budget per influencer is ${budget_per_influencer} ${currency}.`;
    }

    if (number_of_influencers) {
      prompt += ` The campaign requires ${number_of_influencers} influencers.`;
    }

    if (is_barter) {
      prompt += ` The campaign is a barter campaign.`;
    }

    if (content_type?.length) {
      prompt += ` The required content types are: ${content_type.join(', ')}.`;
    }

    if (influencer_type?.length) {
      prompt += ` The targeted influencer type is: ${influencer_type.join(', ')}.`;
    }

    if (job_requirement?.length) {
      prompt += ` The job requirements include: ${job_requirement.join(', ')}.`;
    }
    if (posting_start_date && posting_end_date) {
      prompt += ` The campaign will run from ${posting_start_date} to ${posting_end_date}.`;
    }

    if (first_draft_date) {
      prompt += ` The first draft is expected by ${first_draft_date}.`;
    }

    if (profile_privacy) {
      prompt += ` The campaign's profile privacy setting is "${profile_privacy}".`;
    }

    if (categories?.length) {
      prompt += ` The campaign falls under the following categories: ${categories.join(', ')}.`;
    }

    if (platforms?.length) {
      prompt += ` The campaign will be executed on these platforms: ${platforms.join(', ')}.`;
    }

    if (campaign_requirements) {
      prompt += ` This text is written by me so provide proper text: ${campaign_requirements}.`;
    }

    prompt += Constants.AI_PROMPT.FORMATTING + 'Provide only description of campaign in two paragraph and Do not add Campaign Briefing:';

    return await this.generateResponse(prompt);
  }

  async createChatPrompt(request: any) {
    let prompt = `${Constants.AI_PROMPT.POLITENESS_CHAT}: ${request.message}.`;
    return await this.generateResponse(prompt);
  }

  async translateKeywordsToKorean(englishKeywords: string[]): Promise<string[]> {
    if (!englishKeywords.length) {
      return [];
    }

    const numbered = englishKeywords
      .map((keyword, index) => `${index + 1}. ${keyword}`)
      .join('\n');

    const prompt =
      'Translate each numbered English keyword below into natural Korean (한국어). ' +
      'Keep influencer/marketing terminology accurate. ' +
      'Return ONLY a valid JSON array of strings with the same length and order as the input. ' +
      'No markdown fences or explanation.\n\n' +
      numbered;

    const raw = await this.generateResponse(prompt);
    const cleaned = raw.trim().replace(/^```(?:json)?\s*/i, '').replace(/\s*```$/i, '');
    const parsed = JSON.parse(cleaned);

    if (!Array.isArray(parsed) || parsed.length !== englishKeywords.length) {
      throw new HttpException(
        'Translation response length does not match input',
        HttpStatus.BAD_GATEWAY,
      );
    }

    return parsed.map((value) => String(value).trim());
  }


  async getLogs(log_id: string) {
    const apiKey = await this.encryptionService.decrypt(process.env.OPENAI_API_KEY);
    // resp_* IDs belong to the Responses API, not chat/completions
    const apiUrl = `https://api.openai.com/v1/responses/${log_id}`;
    try {
      const response = await axios.get(apiUrl, {
        headers: {
          'Authorization': `Bearer ${apiKey}`,
          'Content-Type': 'application/json',
        },
      });
      return response.data;
    } catch (error: any) {
      console.error('Error calling OpenAI API:', error);
      throw new HttpException(error.message, HttpStatus.BAD_REQUEST);
    }
  }

  /**
   * Generates an AI agent applicant report from a frozen request snapshot.
   * Returns parsed JSON plus provider usage metadata for ai_report_log.
   */
  async createAiAgentReport(requestJson: any) {
    const apiKey = await this.encryptionService.decrypt(process.env.OPENAI_API_KEY);
    const openai = new OpenAI({ apiKey });
    const model = await this.getAiPrompt(KeyType.AI_MODEL, Constants.AI_PROMPT.MODEL);

    const payload = this.normalizeRequestJson(requestJson);
    const influencers = Array.isArray(payload?.influencers) ? payload.influencers : [];
    const expectedCount = influencers.length;

    const requestJsonText = JSON.stringify(payload);

    const promptTemplate = await this.getAiPrompt(KeyType.AI_AGENT_REPORT, Constants.AI_PROMPT.AI_AGENT_REPORT);
    let prompt = this.applyPromptVars(promptTemplate, { expectedCount });

    try {
      const response = await openai.chat.completions.create({
        model,
        messages: [
          { role: 'system', content: prompt },
          { role: 'user', content: requestJsonText },
        ],
        // temperature: 0.2,
        response_format: {
          type: 'json_schema',
          json_schema: {
            name: 'ai_agent_report',
            strict: true,
            schema: Constants.AI_AGENT_REPORT_JSON_SCHEMA,
          },
        },
        //  max_tokens: 16384,
      });

      const raw = response.choices?.[0]?.message?.content ?? '';
      const cleaned = String(raw).trim().replace(/^```(?:json)?\s*/i, '').replace(/\s*```$/i, '');
      let content: Record<string, any>;
      try {
        content = JSON.parse(cleaned);
      } catch (error: any) {
        console.log(error);
        throw new HttpException('AI report response is not valid JSON', HttpStatus.BAD_GATEWAY);
      }

      // const scored = Array.isArray(content?.influencers) ? content.influencers : [];
      // if (scored.length < expectedCount) {
      //   throw new HttpException(
      //     `AI returned ${scored.length}/${expectedCount} influencers. Incomplete response.`,
      //     HttpStatus.BAD_GATEWAY,
      //   );
      // }

      const usage = response.usage;
      const inputToken = usage?.prompt_tokens ?? 0;
      const outputToken = usage?.completion_tokens ?? 0;
      console.log(response);
      return {
        content,
        raw,
        response_id: response.id ?? null,
        model_name: response.model ?? model,
        input_token: inputToken,
        output_token: outputToken,
        total_token: usage?.total_tokens ?? (inputToken + outputToken),
      };
    } catch (error: any) {
      if (error instanceof HttpException) {
        throw error;
      }
      console.error('Error calling OpenAI API for AI agent report:', error?.error || error.message);
      throw new HttpException(
        error?.error?.message || error.message || 'AI report generation failed',
        error?.status === 429 ? HttpStatus.TOO_MANY_REQUESTS : HttpStatus.BAD_REQUEST,
      );
    }
  }

  private async getAiPrompt(type: KeyType, fallback: string): Promise<string> {
    try {
      const setting = await this.Settings.findOne({ where: { type } });
      const value = setting?.value?.trim();
      return value || fallback;
    } catch (error) {
      return fallback;
    }
  }

  private normalizeRequestJson(requestJson: any): Record<string, any> {
    if (requestJson == null) {
      return { influencers: [] };
    }
    if (typeof requestJson === 'string') {
      try {
        return JSON.parse(requestJson);
      } catch {
        return { influencers: [] };
      }
    }
    return requestJson;
  }

  private applyPromptVars(template: string, vars: Record<string, string | number>): string {
    return Object.entries(vars).reduce((prompt, [key, value]) => {
      // split/join avoids String.replaceAll `$` patterns inside JSON payloads
      // (`$'`, `$&`, `$``) which can wipe INPUT_JSON / RESPONSE_SCHEMA.
      return prompt.split(`{{${key}}}`).join(String(value ?? ''));
    }, template);
  }

}
