/**
 * ProjectStageService - Kanban Stage Management Service
 *
 * Provides stage/column management for project task boards.
 * Supports multi-tenant architecture with ServiceContext pattern.
 *
 * @module Projects
 */

import { DataSource, Repository } from 'typeorm';
import { ProjectStageEntity } from '../entities/project-stage.entity';
import { ServiceContext, PaginatedResult } from './project.service';

export interface CreateStageDto {
  projectId?: string;
  name: string;
  sequence?: number;
  fold?: boolean;
  isClosed?: boolean;
}

export interface UpdateStageDto {
  name?: string;
  sequence?: number;
  fold?: boolean;
  isClosed?: boolean;
}

export interface StageFilters {
  projectId?: string | null;
  isClosed?: boolean;
}

export class ProjectStageService {
  private repository: Repository<ProjectStageEntity>;

  constructor(dataSource: DataSource) {
    this.repository = dataSource.getRepository(ProjectStageEntity);
  }

  /**
   * Find stage by ID
   */
  async findById(ctx: ServiceContext, id: string): Promise<ProjectStageEntity | null> {
    return this.repository.findOne({
      where: {
        id,
        tenantId: ctx.tenantId,
      },
    });
  }

  /**
   * Find all stages with pagination and filters
   */
  async findAll(
    ctx: ServiceContext,
    filters: StageFilters = {},
    page = 1,
    limit = 50
  ): Promise<PaginatedResult<ProjectStageEntity>> {
    const skip = (page - 1) * limit;

    const qb = this.repository
      .createQueryBuilder('s')
      .where('s.tenant_id = :tenantId', { tenantId: ctx.tenantId });

    if (filters.projectId !== undefined) {
      if (filters.projectId === null) {
        qb.andWhere('s.project_id IS NULL');
      } else {
        qb.andWhere('(s.project_id = :projectId OR s.project_id IS NULL)', {
          projectId: filters.projectId,
        });
      }
    }

    if (filters.isClosed !== undefined) {
      qb.andWhere('s.is_closed = :isClosed', { isClosed: filters.isClosed });
    }

    qb.orderBy('s.sequence', 'ASC').addOrderBy('s.created_at', 'ASC').skip(skip).take(limit);

    const [data, total] = await qb.getManyAndCount();

    return {
      data,
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
    };
  }

  /**
   * Create a new stage
   */
  async create(ctx: ServiceContext, dto: CreateStageDto): Promise<ProjectStageEntity> {
    // Get next sequence
    let sequence = dto.sequence;
    if (sequence === undefined) {
      const maxSeq = await this.repository
        .createQueryBuilder('s')
        .select('COALESCE(MAX(s.sequence), 0) + 1', 'nextSeq')
        .where('s.tenant_id = :tenantId', { tenantId: ctx.tenantId })
        .andWhere(
          dto.projectId ? 's.project_id = :projectId' : 's.project_id IS NULL',
          dto.projectId ? { projectId: dto.projectId } : {}
        )
        .getRawOne();
      sequence = parseInt(maxSeq?.nextSeq || '1');
    }

    const entity = this.repository.create({
      tenantId: ctx.tenantId,
      projectId: dto.projectId || null,
      name: dto.name,
      sequence,
      fold: dto.fold ?? false,
      isClosed: dto.isClosed ?? false,
    });

    return this.repository.save(entity);
  }

  /**
   * Update a stage
   */
  async update(
    ctx: ServiceContext,
    id: string,
    dto: UpdateStageDto
  ): Promise<ProjectStageEntity | null> {
    const entity = await this.findById(ctx, id);
    if (!entity) {
      return null;
    }

    if (dto.name !== undefined) entity.name = dto.name;
    if (dto.sequence !== undefined) entity.sequence = dto.sequence;
    if (dto.fold !== undefined) entity.fold = dto.fold;
    if (dto.isClosed !== undefined) entity.isClosed = dto.isClosed;

    return this.repository.save(entity);
  }

  /**
   * Delete a stage
   */
  async delete(ctx: ServiceContext, id: string): Promise<boolean> {
    const entity = await this.findById(ctx, id);
    if (!entity) {
      return false;
    }

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

  /**
   * Find global stages (not project-specific)
   */
  async findGlobalStages(
    ctx: ServiceContext,
    page = 1,
    limit = 50
  ): Promise<PaginatedResult<ProjectStageEntity>> {
    return this.findAll(ctx, { projectId: null }, page, limit);
  }

  /**
   * Find stages for a project (includes global + project-specific)
   */
  async findByProject(
    ctx: ServiceContext,
    projectId: string,
    page = 1,
    limit = 50
  ): Promise<PaginatedResult<ProjectStageEntity>> {
    return this.findAll(ctx, { projectId }, page, limit);
  }

  /**
   * Reorder stages
   */
  async reorder(
    ctx: ServiceContext,
    stageIds: string[]
  ): Promise<ProjectStageEntity[]> {
    const updated: ProjectStageEntity[] = [];

    for (let i = 0; i < stageIds.length; i++) {
      const stage = await this.update(ctx, stageIds[i], { sequence: i + 1 });
      if (stage) {
        updated.push(stage);
      }
    }

    return updated;
  }

  /**
   * Create default stages for a project
   */
  async createDefaultStages(
    ctx: ServiceContext,
    projectId?: string
  ): Promise<ProjectStageEntity[]> {
    const defaultStages = [
      { name: 'Backlog', sequence: 1, fold: true, isClosed: false },
      { name: 'To Do', sequence: 2, fold: false, isClosed: false },
      { name: 'In Progress', sequence: 3, fold: false, isClosed: false },
      { name: 'Review', sequence: 4, fold: false, isClosed: false },
      { name: 'Done', sequence: 5, fold: false, isClosed: true },
    ];

    const stages: ProjectStageEntity[] = [];
    for (const stage of defaultStages) {
      const created = await this.create(ctx, {
        projectId,
        ...stage,
      });
      stages.push(created);
    }

    return stages;
  }

  /**
   * Toggle fold state
   */
  async toggleFold(ctx: ServiceContext, id: string): Promise<ProjectStageEntity | null> {
    const entity = await this.findById(ctx, id);
    if (!entity) {
      return null;
    }

    return this.update(ctx, id, { fold: !entity.fold });
  }

  /**
   * Count stages for a project
   */
  async countStages(ctx: ServiceContext, projectId?: string): Promise<number> {
    const qb = this.repository
      .createQueryBuilder('s')
      .where('s.tenant_id = :tenantId', { tenantId: ctx.tenantId });

    if (projectId) {
      qb.andWhere('(s.project_id = :projectId OR s.project_id IS NULL)', { projectId });
    } else {
      qb.andWhere('s.project_id IS NULL');
    }

    return qb.getCount();
  }
}
