/**
 * Custom Report Service
 * Manages user-customized reports based on existing definitions
 *
 * @module Reports
 */

import { Repository, FindOptionsWhere, ILike } from 'typeorm';
import { CustomReport } from '../entities/custom-report.entity';

export interface CustomReportSearchParams {
  tenantId: string;
  ownerId?: string;
  baseDefinitionId?: string;
  search?: string;
  isFavorite?: boolean;
  limit?: number;
  offset?: number;
}

export interface CreateCustomReportDto {
  ownerId: string;
  baseDefinitionId?: string | null;
  name: string;
  description?: string | null;
  customColumns?: Record<string, any>[];
  customFilters?: Record<string, any>[];
  customGrouping?: Record<string, any>[];
  customSorting?: Record<string, any>[];
  isFavorite?: boolean;
}

export interface UpdateCustomReportDto extends Partial<Omit<CreateCustomReportDto, 'ownerId'>> {}

export class CustomReportService {
  constructor(private readonly customReportRepository: Repository<CustomReport>) {}

  // ==================== CRUD Operations ====================

  async findAll(params: CustomReportSearchParams): Promise<{ data: CustomReport[]; total: number }> {
    const {
      tenantId,
      ownerId,
      baseDefinitionId,
      search,
      isFavorite,
      limit = 50,
      offset = 0,
    } = params;

    const where: FindOptionsWhere<CustomReport>[] = [];
    const baseWhere: FindOptionsWhere<CustomReport> = { tenantId };

    if (ownerId) {
      baseWhere.ownerId = ownerId;
    }

    if (baseDefinitionId) {
      baseWhere.baseDefinitionId = baseDefinitionId;
    }

    if (isFavorite !== undefined) {
      baseWhere.isFavorite = isFavorite;
    }

    if (search) {
      where.push(
        { ...baseWhere, name: ILike(`%${search}%`) },
        { ...baseWhere, description: ILike(`%${search}%`) }
      );
    } else {
      where.push(baseWhere);
    }

    const [data, total] = await this.customReportRepository.findAndCount({
      where,
      relations: ['baseDefinition'],
      take: limit,
      skip: offset,
      order: { isFavorite: 'DESC', name: 'ASC' },
    });

    return { data, total };
  }

  async findOne(id: string, tenantId: string): Promise<CustomReport | null> {
    return this.customReportRepository.findOne({
      where: { id, tenantId },
      relations: ['baseDefinition'],
    });
  }

  async findByOwner(
    ownerId: string,
    tenantId: string,
    options?: {
      baseDefinitionId?: string;
      isFavorite?: boolean;
      limit?: number;
    }
  ): Promise<CustomReport[]> {
    const where: FindOptionsWhere<CustomReport> = { ownerId, tenantId };

    if (options?.baseDefinitionId) {
      where.baseDefinitionId = options.baseDefinitionId;
    }

    if (options?.isFavorite !== undefined) {
      where.isFavorite = options.isFavorite;
    }

    return this.customReportRepository.find({
      where,
      relations: ['baseDefinition'],
      order: { isFavorite: 'DESC', name: 'ASC' },
      take: options?.limit,
    });
  }

  async create(tenantId: string, dto: CreateCustomReportDto): Promise<CustomReport> {
    const customReport = this.customReportRepository.create({
      ...dto,
      tenantId,
    });

    return this.customReportRepository.save(customReport);
  }

  async update(
    id: string,
    tenantId: string,
    ownerId: string,
    dto: UpdateCustomReportDto
  ): Promise<CustomReport | null> {
    const customReport = await this.customReportRepository.findOne({
      where: { id, tenantId, ownerId },
    });

    if (!customReport) return null;

    Object.assign(customReport, dto);

    return this.customReportRepository.save(customReport);
  }

  async delete(id: string, tenantId: string, ownerId: string): Promise<boolean> {
    const customReport = await this.customReportRepository.findOne({
      where: { id, tenantId, ownerId },
    });

    if (!customReport) return false;

    await this.customReportRepository.delete(id);
    return true;
  }

  // ==================== Favorite Operations ====================

  async toggleFavorite(
    id: string,
    tenantId: string,
    ownerId: string
  ): Promise<CustomReport | null> {
    const customReport = await this.customReportRepository.findOne({
      where: { id, tenantId, ownerId },
    });

    if (!customReport) return null;

    customReport.isFavorite = !customReport.isFavorite;

    return this.customReportRepository.save(customReport);
  }

  async getFavorites(ownerId: string, tenantId: string): Promise<CustomReport[]> {
    return this.customReportRepository.find({
      where: { ownerId, tenantId, isFavorite: true },
      relations: ['baseDefinition'],
      order: { name: 'ASC' },
    });
  }

  // ==================== Clone Operations ====================

  async clone(
    id: string,
    tenantId: string,
    ownerId: string,
    newName: string
  ): Promise<CustomReport> {
    const source = await this.findOne(id, tenantId);
    if (!source) {
      throw new Error('Source custom report not found');
    }

    const cloned = this.customReportRepository.create({
      tenantId,
      ownerId,
      baseDefinitionId: source.baseDefinitionId,
      name: newName,
      description: source.description,
      customColumns: [...source.customColumns],
      customFilters: [...source.customFilters],
      customGrouping: [...source.customGrouping],
      customSorting: [...source.customSorting],
      isFavorite: false,
    });

    return this.customReportRepository.save(cloned);
  }

  // ==================== Column Configuration ====================

  async updateColumns(
    id: string,
    tenantId: string,
    ownerId: string,
    columns: Record<string, any>[]
  ): Promise<CustomReport | null> {
    const customReport = await this.customReportRepository.findOne({
      where: { id, tenantId, ownerId },
    });

    if (!customReport) return null;

    customReport.customColumns = columns;

    return this.customReportRepository.save(customReport);
  }

  async updateFilters(
    id: string,
    tenantId: string,
    ownerId: string,
    filters: Record<string, any>[]
  ): Promise<CustomReport | null> {
    const customReport = await this.customReportRepository.findOne({
      where: { id, tenantId, ownerId },
    });

    if (!customReport) return null;

    customReport.customFilters = filters;

    return this.customReportRepository.save(customReport);
  }

  async updateGrouping(
    id: string,
    tenantId: string,
    ownerId: string,
    grouping: Record<string, any>[]
  ): Promise<CustomReport | null> {
    const customReport = await this.customReportRepository.findOne({
      where: { id, tenantId, ownerId },
    });

    if (!customReport) return null;

    customReport.customGrouping = grouping;

    return this.customReportRepository.save(customReport);
  }

  async updateSorting(
    id: string,
    tenantId: string,
    ownerId: string,
    sorting: Record<string, any>[]
  ): Promise<CustomReport | null> {
    const customReport = await this.customReportRepository.findOne({
      where: { id, tenantId, ownerId },
    });

    if (!customReport) return null;

    customReport.customSorting = sorting;

    return this.customReportRepository.save(customReport);
  }

  // ==================== History Operations ====================

  async getHistory(
    tenantId: string,
    ownerId: string,
    limit = 20
  ): Promise<CustomReport[]> {
    return this.customReportRepository.find({
      where: { tenantId, ownerId },
      relations: ['baseDefinition'],
      order: { updatedAt: 'DESC' },
      take: limit,
    });
  }

  // ==================== Export Operations ====================

  async export(
    id: string,
    tenantId: string,
    ownerId: string,
    format: 'pdf' | 'excel' | 'csv'
  ): Promise<{ filePath: string; mimeType: string }> {
    const customReport = await this.customReportRepository.findOne({
      where: { id, tenantId, ownerId },
    });

    if (!customReport) {
      throw new Error('Custom report not found');
    }

    // This would integrate with actual export logic
    const mimeTypes: Record<string, string> = {
      pdf: 'application/pdf',
      excel: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
      csv: 'text/csv',
    };

    return {
      filePath: `/exports/custom_report_${id}_${Date.now()}.${format}`,
      mimeType: mimeTypes[format],
    };
  }

  // ==================== Generate Report ====================

  async generate(
    id: string,
    tenantId: string,
    ownerId: string,
    parameters?: Record<string, any>
  ): Promise<{ data: any[]; rowCount: number }> {
    const customReport = await this.customReportRepository.findOne({
      where: { id, tenantId, ownerId },
      relations: ['baseDefinition'],
    });

    if (!customReport) {
      throw new Error('Custom report not found');
    }

    // This would integrate with actual report generation logic
    // combining base definition query with custom columns, filters, grouping, sorting
    return {
      data: [],
      rowCount: 0,
    };
  }

  async schedule(
    id: string,
    tenantId: string,
    ownerId: string,
    cronExpression: string
  ): Promise<void> {
    const customReport = await this.customReportRepository.findOne({
      where: { id, tenantId, ownerId },
    });

    if (!customReport) {
      throw new Error('Custom report not found');
    }

    // This would integrate with the report schedule service
    // to create a schedule for this custom report
    // For now, this is a placeholder
  }
}
