import { Injectable } from '@nestjs/common';
import { PrismaService } from '@modules/shared';

/**
 * SystemSettings service — read/write the SystemSetting key/value store for
 * runtime-configurable platform settings (currently: Claude/Anthropic AI
 * credentials).
 *
 * TODO(hardening): secret values are persisted as plain strings. A future
 * iteration should encrypt `isSecret` entries at rest (libsodium / KMS).
 */

export const AI_SETTING_KEYS = {
  apiUrl: 'ai.api_url',
  apiKey: 'ai.api_key',
  model: 'ai.model',
} as const;

export const AI_DEFAULTS = {
  apiUrl: 'https://api.anthropic.com/v1',
  model: 'claude-opus-4-5',
} as const;

export interface AiSettingsInternal {
  apiUrl: string;
  apiKey: string | null;
  model: string;
  updatedAt: Date | null;
}

export interface UpdateAiSettingsInput {
  apiUrl?: string;
  apiKey?: string; // pass empty string to clear, '__UNCHANGED__' to leave, undefined to leave
  model?: string;
  updatedBy?: string | null;
}

export const UNCHANGED_SENTINEL = '__UNCHANGED__';

@Injectable()
export class SystemSettingsService {
  constructor(private readonly prisma: PrismaService) {}

  /**
   * Read the current AI settings including the raw (unmasked) API key. Intended
   * for backend runtime consumers only — never return the raw key over HTTP.
   */
  async getAiSettings(): Promise<AiSettingsInternal> {
    const rows = await this.prisma.systemSetting.findMany({
      where: {
        key: {
          in: [AI_SETTING_KEYS.apiUrl, AI_SETTING_KEYS.apiKey, AI_SETTING_KEYS.model],
        },
      },
    });

    const byKey = new Map(rows.map((r) => [r.key, r]));
    const apiUrlRow = byKey.get(AI_SETTING_KEYS.apiUrl);
    const apiKeyRow = byKey.get(AI_SETTING_KEYS.apiKey);
    const modelRow = byKey.get(AI_SETTING_KEYS.model);

    const latestUpdatedAt = rows.reduce<Date | null>((acc, r) => {
      if (!acc || r.updatedAt > acc) return r.updatedAt;
      return acc;
    }, null);

    return {
      apiUrl: apiUrlRow?.value || AI_DEFAULTS.apiUrl,
      apiKey: apiKeyRow?.value || null,
      model: modelRow?.value || AI_DEFAULTS.model,
      updatedAt: latestUpdatedAt,
    };
  }

  async updateAiSettings(input: UpdateAiSettingsInput): Promise<AiSettingsInternal> {
    const updatedBy = input.updatedBy ?? null;
    const ops: Array<Promise<unknown>> = [];

    if (input.apiUrl !== undefined) {
      ops.push(
        this.prisma.systemSetting.upsert({
          where: { key: AI_SETTING_KEYS.apiUrl },
          create: {
            key: AI_SETTING_KEYS.apiUrl,
            value: input.apiUrl,
            valueType: 'string',
            isSecret: false,
            updatedBy,
          },
          update: { value: input.apiUrl, valueType: 'string', isSecret: false, updatedBy },
        }),
      );
    }

    if (input.model !== undefined) {
      ops.push(
        this.prisma.systemSetting.upsert({
          where: { key: AI_SETTING_KEYS.model },
          create: {
            key: AI_SETTING_KEYS.model,
            value: input.model,
            valueType: 'string',
            isSecret: false,
            updatedBy,
          },
          update: { value: input.model, valueType: 'string', isSecret: false, updatedBy },
        }),
      );
    }

    // apiKey semantics:
    //   - undefined  → do nothing
    //   - '__UNCHANGED__' → do nothing (frontend round-trip sentinel)
    //   - '' (empty) → explicit clear
    //   - any other string → overwrite
    if (input.apiKey !== undefined && input.apiKey !== UNCHANGED_SENTINEL) {
      if (input.apiKey === '') {
        ops.push(
          this.prisma.systemSetting.deleteMany({ where: { key: AI_SETTING_KEYS.apiKey } }),
        );
      } else {
        ops.push(
          this.prisma.systemSetting.upsert({
            where: { key: AI_SETTING_KEYS.apiKey },
            create: {
              key: AI_SETTING_KEYS.apiKey,
              value: input.apiKey,
              valueType: 'secret',
              isSecret: true,
              updatedBy,
            },
            update: {
              value: input.apiKey,
              valueType: 'secret',
              isSecret: true,
              updatedBy,
            },
          }),
        );
      }
    }

    await Promise.all(ops);
    return this.getAiSettings();
  }

  /**
   * Mask an Anthropic API key: keep first 7 chars + `...` + last 4 chars.
   * Example: `sk-ant-api03-abc...wxyz`  →  `sk-ant-...wxyz`.
   */
  static maskApiKey(raw: string | null): string | null {
    if (!raw) return null;
    if (raw.length <= 11) {
      // Too short to meaningfully mask — still hide the middle.
      return `${raw.slice(0, Math.min(4, raw.length))}...`;
    }
    return `${raw.slice(0, 7)}...${raw.slice(-4)}`;
  }
}
