import { Injectable } from "@nestjs/common";
import { Prisma } from "@prisma/client";
import { PrismaService } from "../prisma/prisma.service";
import type { WorkspaceLlmSettings, LlmProviderInstance, Personality } from "@prisma/client";
import type { UpdateWorkspaceSettingsDto } from "./dto";

/**
 * Service for managing workspace LLM settings
 * Handles configuration hierarchy: workspace > user > system
 */
@Injectable()
export class WorkspaceSettingsService {
  constructor(private readonly prisma: PrismaService) {}

  /**
   * Get settings for a workspace (creates default if not exists)
   *
   * @param workspaceId - Workspace ID
   * @returns Workspace LLM settings
   */
  async getSettings(workspaceId: string): Promise<WorkspaceLlmSettings> {
    let settings = await this.prisma.workspaceLlmSettings.findUnique({
      where: { workspaceId },
    });

    // Create default settings if they don't exist
    settings ??= await this.prisma.workspaceLlmSettings.create({
      data: {
        workspaceId,
        settings: {} as unknown as Prisma.InputJsonValue,
      },
    });

    return settings;
  }

  /**
   * Update workspace LLM settings
   *
   * @param workspaceId - Workspace ID
   * @param dto - Update data
   * @returns Updated settings
   */
  async updateSettings(
    workspaceId: string,
    dto: UpdateWorkspaceSettingsDto
  ): Promise<WorkspaceLlmSettings> {
    const data: Prisma.WorkspaceLlmSettingsUncheckedUpdateInput = {};

    if (dto.defaultLlmProviderId !== undefined) {
      data.defaultLlmProviderId = dto.defaultLlmProviderId;
    }

    if (dto.defaultPersonalityId !== undefined) {
      data.defaultPersonalityId = dto.defaultPersonalityId;
    }

    if (dto.settings !== undefined) {
      data.settings = dto.settings as unknown as Prisma.InputJsonValue;
    }

    const settings = await this.prisma.workspaceLlmSettings.update({
      where: { workspaceId },
      data,
    });

    return settings;
  }

  /**
   * Get effective LLM provider for a workspace
   * Priority: workspace > user > system default
   *
   * @param workspaceId - Workspace ID
   * @param userId - Optional user ID for user-level provider
   * @returns Effective LLM provider instance
   * @throws {Error} If no provider available
   */
  async getEffectiveLlmProvider(
    workspaceId: string,
    userId?: string
  ): Promise<LlmProviderInstance> {
    // Get workspace settings
    const settings = await this.prisma.workspaceLlmSettings.findUnique({
      where: { workspaceId },
    });

    // 1. Check workspace-level provider
    if (settings?.defaultLlmProviderId) {
      const provider = await this.prisma.llmProviderInstance.findUnique({
        where: { id: settings.defaultLlmProviderId },
      });

      if (!provider) {
        throw new Error(`LLM provider ${settings.defaultLlmProviderId} not found`);
      }

      return provider;
    }

    // 2. Check user-level provider
    if (userId) {
      const userProvider = await this.prisma.llmProviderInstance.findFirst({
        where: {
          userId,
          isEnabled: true,
        },
      });

      if (userProvider) {
        return userProvider;
      }
    }

    // 3. Fall back to system default
    const systemDefault = await this.prisma.llmProviderInstance.findFirst({
      where: {
        userId: null,
        isDefault: true,
        isEnabled: true,
      },
    });

    if (!systemDefault) {
      throw new Error(`No LLM provider available for workspace ${workspaceId}`);
    }

    return systemDefault;
  }

  /**
   * Get effective personality for a workspace
   * Priority: workspace default > workspace enabled > any enabled
   *
   * @param workspaceId - Workspace ID
   * @returns Effective personality
   * @throws {Error} If no personality available
   */
  async getEffectivePersonality(workspaceId: string): Promise<Personality> {
    // Get workspace settings
    const settings = await this.prisma.workspaceLlmSettings.findUnique({
      where: { workspaceId },
    });

    // 1. Check workspace-configured personality
    if (settings?.defaultPersonalityId) {
      const personality = await this.prisma.personality.findUnique({
        where: {
          id: settings.defaultPersonalityId,
        },
      });

      if (!personality) {
        throw new Error(`Personality ${settings.defaultPersonalityId} not found`);
      }

      return personality;
    }

    // 2. Fall back to default personality in workspace
    const defaultPersonality = await this.prisma.personality.findFirst({
      where: {
        workspaceId,
        isDefault: true,
        isEnabled: true,
      },
    });

    if (defaultPersonality) {
      return defaultPersonality;
    }

    // 3. Fall back to any enabled personality
    const anyPersonality = await this.prisma.personality.findFirst({
      where: {
        workspaceId,
        isEnabled: true,
      },
    });

    if (!anyPersonality) {
      throw new Error(`No personality available for workspace ${workspaceId}`);
    }

    return anyPersonality;
  }
}
