import { logger } from '@goodgo/logger';
import { PrismaClient } from '@prisma/client';
import { Request } from 'express';

import { getPrismaClient } from '../../../config/database.config';
import { auditService } from '../../../core/events/audit.service';
import { NotFoundError, ConflictError } from '../../../errors/http-error';
import { OrganizationRepository } from '../../../repositories/organization.repository';
import { CreateOrganizationDto, UpdateOrganizationDto } from '../identity.dto';

/**
 * EN: Organization Service
 * VI: Service tổ chức
 */
export class OrganizationService {
  private prisma: PrismaClient;
  private orgRepo: OrganizationRepository;

  constructor() {
    this.prisma = getPrismaClient();
    this.orgRepo = new OrganizationRepository(this.prisma);
  }

  /**
   * EN: Create organization
   * VI: Tạo tổ chức
   */
  async create(data: CreateOrganizationDto, req?: Request): Promise<any> {
    // Check if domain already exists
    if (data.domain) {
      const existing = await this.orgRepo.findByDomain(data.domain);
      if (existing) {
        throw new ConflictError('Organization domain already exists / Domain tổ chức đã tồn tại');
      }
    }

    const organization = await this.orgRepo.create({
      name: data.name,
      domain: data.domain || null,
      parentId: data.parentId || null,
      settings: data.settings || null,
      isActive: true,
    });

    if (req) {
      await auditService.logFromRequest(req, 'ORGANIZATION_CREATED', {
        organizationId: organization.id,
        success: true,
      });
    }

    logger.info('Organization created', { organizationId: organization.id });

    return organization;
  }

  /**
   * EN: Get organization by ID
   * VI: Lấy tổ chức theo ID
   */
  async getById(id: string): Promise<any> {
    const org = await this.orgRepo.findWithHierarchy(id);
    if (!org) {
      throw new NotFoundError('Organization');
    }
    return org;
  }

  /**
   * EN: Get organization by domain
   * VI: Lấy tổ chức theo domain
   */
  async getByDomain(domain: string): Promise<any> {
    const org = await this.orgRepo.findByDomain(domain);
    if (!org) {
      throw new NotFoundError('Organization');
    }
    return org;
  }

  /**
   * EN: Update organization
   * VI: Cập nhật tổ chức
   */
  async update(id: string, data: UpdateOrganizationDto, req?: Request): Promise<any> {
    const org = await this.orgRepo.findById(id);
    if (!org) {
      throw new NotFoundError('Organization');
    }

    // Check domain conflict
    if (data.domain && data.domain !== org.domain) {
      const existing = await this.orgRepo.findByDomain(data.domain);
      if (existing) {
        throw new ConflictError('Organization domain already exists / Domain tổ chức đã tồn tại');
      }
    }

    const updated = await this.orgRepo.update(id, {
      ...(data.name && { name: data.name }),
      ...(data.domain !== undefined && { domain: data.domain }),
      ...(data.parentId !== undefined && { parentId: data.parentId }),
      ...(data.settings !== undefined && { settings: data.settings }),
      ...(data.isActive !== undefined && { isActive: data.isActive }),
    });

    if (req) {
      await auditService.logFromRequest(req, 'ORGANIZATION_UPDATED', {
        organizationId: updated.id,
        changes: data,
        success: true,
      });
    }

    logger.info('Organization updated', { organizationId: updated.id });

    return updated;
  }

  /**
   * EN: Delete organization
   * VI: Xóa tổ chức
   */
  async delete(id: string, req?: Request): Promise<boolean> {
    const org = await this.orgRepo.findById(id);
    if (!org) {
      throw new NotFoundError('Organization');
    }

    // Check if organization has children
    const children = await this.orgRepo.findChildren(id);
    if (children.length > 0) {
      throw new ConflictError('Cannot delete organization with child organizations / Không thể xóa tổ chức có tổ chức con');
    }

    // Check if organization has users
    const userCount = await this.orgRepo.getUserCount(id);
    if (userCount > 0) {
      throw new ConflictError('Cannot delete organization with users / Không thể xóa tổ chức có users');
    }

    await this.orgRepo.delete(id);

    if (req) {
      await auditService.logFromRequest(req, 'ORGANIZATION_DELETED', {
        organizationId: id,
        success: true,
      });
    }

    logger.info('Organization deleted', { organizationId: id });

    return true;
  }

  /**
   * EN: Get child organizations
   * VI: Lấy các tổ chức con
   */
  async getChildren(parentId: string): Promise<any[]> {
    return this.orgRepo.findChildren(parentId);
  }

  /**
   * EN: Get organization users
   * VI: Lấy users của tổ chức
   */
  async getUsers(organizationId: string, skip: number = 0, take: number = 20): Promise<{ users: any[]; total: number }> {
    const [users, total] = await Promise.all([
      this.prisma.user.findMany({
        where: { organizationId },
        skip,
        take,
        include: {
          profile: true,
        },
        orderBy: { createdAt: 'desc' },
      }),
      this.prisma.user.count({ where: { organizationId } }),
    ]);

    return { users, total };
  }

  /**
   * EN: Add user to organization
   * VI: Thêm user vào tổ chức
   */
  async addUser(organizationId: string, userId: string, req?: Request): Promise<any> {
    const org = await this.orgRepo.findById(organizationId);
    if (!org) {
      throw new NotFoundError('Organization');
    }

    const user = await this.prisma.user.findUnique({ where: { id: userId } });
    if (!user) {
      throw new NotFoundError('User');
    }

    const updated = await this.prisma.user.update({
      where: { id: userId },
      data: { organizationId },
    });

    if (req) {
      await auditService.logFromRequest(req, 'USER_ADDED_TO_ORGANIZATION', {
        organizationId,
        userId,
        success: true,
      });
    }

    logger.info('User added to organization', { organizationId, userId });

    return updated;
  }

  /**
   * EN: Remove user from organization
   * VI: Xóa user khỏi tổ chức
   */
  async removeUser(organizationId: string, userId: string, req?: Request): Promise<any> {
    const user = await this.prisma.user.findUnique({ where: { id: userId } });
    if (!user) {
      throw new NotFoundError('User');
    }

    if (user.organizationId !== organizationId) {
      throw new NotFoundError('User not in organization / User không thuộc tổ chức');
    }

    const updated = await this.prisma.user.update({
      where: { id: userId },
      data: { organizationId: null },
    });

    if (req) {
      await auditService.logFromRequest(req, 'USER_REMOVED_FROM_ORGANIZATION', {
        organizationId,
        userId,
        success: true,
      });
    }

    logger.info('User removed from organization', { organizationId, userId });

    return updated;
  }

  /**
   * EN: List all organizations
   * VI: Liệt kê tất cả tổ chức
   */
  async list(skip: number = 0, take: number = 20): Promise<{ organizations: any[]; total: number }> {
    const [organizations, total] = await Promise.all([
      this.orgRepo.findAll({
        skip,
        take,
        orderBy: { createdAt: 'desc' },
        include: {
          parent: {
            select: {
              id: true,
              name: true,
            },
          },
          _count: {
            select: {
              users: true,
              groups: true,
            },
          },
        },
      }),
      this.orgRepo.count({}),
    ]);

    return { organizations, total };
  }
}

export const organizationService = new OrganizationService();
