import { Injectable, NotFoundException, ConflictException, Logger } from "@nestjs/common";
import type { FormalityLevel, Personality } from "@prisma/client";
import { PrismaService } from "../prisma/prisma.service";
import type { CreatePersonalityDto } from "./dto/create-personality.dto";
import type { UpdatePersonalityDto } from "./dto/update-personality.dto";
import type { PersonalityQueryDto } from "./dto/personality-query.dto";
import type { PersonalityResponse } from "./entities/personality.entity";

/**
 * Service for managing personality/assistant configurations.
 *
 * Field mapping:
 *   Prisma `systemPrompt`  <-> API/frontend `systemPromptTemplate`
 *   Prisma `isEnabled`     <-> API/frontend `isActive`
 */
@Injectable()
export class PersonalitiesService {
  private readonly logger = new Logger(PersonalitiesService.name);

  constructor(private readonly prisma: PrismaService) {}

  /**
   * Map a Prisma Personality record to the API response shape.
   */
  private toResponse(personality: Personality): PersonalityResponse {
    return {
      id: personality.id,
      workspaceId: personality.workspaceId,
      name: personality.name,
      description: personality.description,
      tone: personality.tone,
      formalityLevel: personality.formalityLevel,
      systemPromptTemplate: personality.systemPrompt,
      isDefault: personality.isDefault,
      isActive: personality.isEnabled,
      createdAt: personality.createdAt,
      updatedAt: personality.updatedAt,
    };
  }

  /**
   * Create a new personality
   */
  async create(workspaceId: string, dto: CreatePersonalityDto): Promise<PersonalityResponse> {
    // Check for duplicate name within workspace
    const existing = await this.prisma.personality.findFirst({
      where: { workspaceId, name: dto.name },
    });

    if (existing) {
      throw new ConflictException(`Personality with name "${dto.name}" already exists`);
    }

    // If creating as default, unset other defaults first
    if (dto.isDefault) {
      await this.unsetOtherDefaults(workspaceId);
    }

    const personality = await this.prisma.personality.create({
      data: {
        workspaceId,
        name: dto.name,
        displayName: dto.name, // use name as displayName since frontend doesn't send displayName separately
        description: dto.description ?? null,
        tone: dto.tone,
        formalityLevel: dto.formalityLevel,
        systemPrompt: dto.systemPromptTemplate,
        isDefault: dto.isDefault ?? false,
        isEnabled: dto.isActive ?? true,
      },
    });

    this.logger.log(`Created personality ${personality.id} for workspace ${workspaceId}`);
    return this.toResponse(personality);
  }

  /**
   * Find all personalities for a workspace with optional active filter
   */
  async findAll(workspaceId: string, query?: PersonalityQueryDto): Promise<PersonalityResponse[]> {
    const where: { workspaceId: string; isEnabled?: boolean } = { workspaceId };

    if (query?.isActive !== undefined) {
      where.isEnabled = query.isActive;
    }

    const personalities = await this.prisma.personality.findMany({
      where,
      orderBy: [{ isDefault: "desc" }, { name: "asc" }],
    });

    return personalities.map((p) => this.toResponse(p));
  }

  /**
   * Find a specific personality by ID
   */
  async findOne(workspaceId: string, id: string): Promise<PersonalityResponse> {
    const personality = await this.prisma.personality.findFirst({
      where: { id, workspaceId },
    });

    if (!personality) {
      throw new NotFoundException(`Personality with ID ${id} not found`);
    }

    return this.toResponse(personality);
  }

  /**
   * Find a personality by name slug
   */
  async findByName(workspaceId: string, name: string): Promise<PersonalityResponse> {
    const personality = await this.prisma.personality.findFirst({
      where: { workspaceId, name },
    });

    if (!personality) {
      throw new NotFoundException(`Personality with name "${name}" not found`);
    }

    return this.toResponse(personality);
  }

  /**
   * Find the default (and enabled) personality for a workspace
   */
  async findDefault(workspaceId: string): Promise<PersonalityResponse> {
    const personality = await this.prisma.personality.findFirst({
      where: { workspaceId, isDefault: true, isEnabled: true },
    });

    if (!personality) {
      throw new NotFoundException(`No default personality found for workspace ${workspaceId}`);
    }

    return this.toResponse(personality);
  }

  /**
   * Update an existing personality
   */
  async update(
    workspaceId: string,
    id: string,
    dto: UpdatePersonalityDto
  ): Promise<PersonalityResponse> {
    // Verify existence
    await this.findOne(workspaceId, id);

    // Check for duplicate name if updating name
    if (dto.name) {
      const existing = await this.prisma.personality.findFirst({
        where: { workspaceId, name: dto.name, id: { not: id } },
      });

      if (existing) {
        throw new ConflictException(`Personality with name "${dto.name}" already exists`);
      }
    }

    // If setting as default, unset other defaults
    if (dto.isDefault === true) {
      await this.unsetOtherDefaults(workspaceId, id);
    }

    // Build update data with field mapping
    const updateData: {
      name?: string;
      displayName?: string;
      description?: string;
      tone?: string;
      formalityLevel?: FormalityLevel;
      systemPrompt?: string;
      isDefault?: boolean;
      isEnabled?: boolean;
    } = {};

    if (dto.name !== undefined) {
      updateData.name = dto.name;
      updateData.displayName = dto.name;
    }
    if (dto.description !== undefined) updateData.description = dto.description;
    if (dto.tone !== undefined) updateData.tone = dto.tone;
    if (dto.formalityLevel !== undefined) updateData.formalityLevel = dto.formalityLevel;
    if (dto.systemPromptTemplate !== undefined) updateData.systemPrompt = dto.systemPromptTemplate;
    if (dto.isDefault !== undefined) updateData.isDefault = dto.isDefault;
    if (dto.isActive !== undefined) updateData.isEnabled = dto.isActive;

    const personality = await this.prisma.personality.update({
      where: { id },
      data: updateData,
    });

    this.logger.log(`Updated personality ${id} for workspace ${workspaceId}`);
    return this.toResponse(personality);
  }

  /**
   * Delete a personality
   */
  async delete(workspaceId: string, id: string): Promise<void> {
    // Verify existence
    await this.findOne(workspaceId, id);

    await this.prisma.personality.delete({
      where: { id },
    });

    this.logger.log(`Deleted personality ${id} from workspace ${workspaceId}`);
  }

  /**
   * Set a personality as the default (convenience endpoint)
   */
  async setDefault(workspaceId: string, id: string): Promise<PersonalityResponse> {
    // Verify existence
    await this.findOne(workspaceId, id);

    // Unset other defaults
    await this.unsetOtherDefaults(workspaceId, id);

    const personality = await this.prisma.personality.update({
      where: { id },
      data: { isDefault: true },
    });

    this.logger.log(`Set personality ${id} as default for workspace ${workspaceId}`);
    return this.toResponse(personality);
  }

  /**
   * Unset the default flag on all other personalities in the workspace
   */
  private async unsetOtherDefaults(workspaceId: string, excludeId?: string): Promise<void> {
    const currentDefault = await this.prisma.personality.findFirst({
      where: {
        workspaceId,
        isDefault: true,
        ...(excludeId !== undefined && { id: { not: excludeId } }),
      },
    });

    if (currentDefault) {
      await this.prisma.personality.update({
        where: { id: currentDefault.id },
        data: { isDefault: false },
      });
    }
  }
}
