import {
  Injectable,
  NotFoundException,
  BadRequestException,
  ForbiddenException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, DataSource, Not } from 'typeorm';

import { Circulation } from './entities/circulation.entity';
import { CirculationRouting } from './entities/circulation-routing.entity';
import { User } from '../user/entities/user.entity';
import { CreateCirculationDto } from './dto/create-circulation.dto';
import { UpdateCirculationRoutingDto } from './dto/update-circulation-routing.dto';
import { SearchCirculationDto } from './dto/search-circulation.dto';
import { DocumentNumberingService } from '../document-numbering/services/document-numbering.service';

@Injectable()
export class CirculationService {
  constructor(
    @InjectRepository(Circulation)
    private circulationRepo: Repository<Circulation>,
    @InjectRepository(CirculationRouting)
    private routingRepo: Repository<CirculationRouting>,
    private numberingService: DocumentNumberingService,
    private dataSource: DataSource
  ) {}

  async create(createDto: CreateCirculationDto, user: User) {
    if (!user.primaryOrganizationId) {
      throw new BadRequestException('User must belong to an organization');
    }

    const queryRunner = this.dataSource.createQueryRunner();
    await queryRunner.connect();
    await queryRunner.startTransaction();

    try {
      // Generate No. using DocumentNumberingService (Type 900 - Circulation)
      const result = await this.numberingService.generateNextNumber({
        projectId: createDto.projectId || 0, // Use projectId from DTO or 0
        originatorOrganizationId: user.primaryOrganizationId,
        typeId: 900, // Fixed Type ID for Circulation
        year: new Date().getFullYear(),
        customTokens: {
          TYPE_CODE: 'CIR',
          ORG_CODE: 'ORG',
        },
      });

      const circulation = queryRunner.manager.create(Circulation, {
        organizationId: user.primaryOrganizationId,
        correspondenceId: createDto.correspondenceId,
        circulationNo: result.number,
        subject: createDto.subject,
        statusCode: 'OPEN',
        createdByUserId: user.user_id,
      });
      const savedCirculation = await queryRunner.manager.save(circulation);

      if (createDto.assigneeIds && createDto.assigneeIds.length > 0) {
        const routings = createDto.assigneeIds.map((userId, index) =>
          queryRunner.manager.create(CirculationRouting, {
            circulationId: savedCirculation.id,
            stepNumber: index + 1,
            organizationId: user.primaryOrganizationId,
            assignedTo: userId,
            status: 'PENDING',
          })
        );
        await queryRunner.manager.save(routings);
      }

      await queryRunner.commitTransaction();
      return savedCirculation;
    } catch (err) {
      await queryRunner.rollbackTransaction();
      throw err;
    } finally {
      await queryRunner.release();
    }
  }

  async findAll(searchDto: SearchCirculationDto, user: User) {
    const { search, status, page = 1, limit = 20 } = searchDto;
    const query = this.circulationRepo
      .createQueryBuilder('c')
      .leftJoinAndSelect('c.creator', 'creator')
      .where('c.organizationId = :orgId', {
        orgId: user.primaryOrganizationId,
      });

    if (status) {
      query.andWhere('c.statusCode = :status', { status });
    }

    query
      .orderBy('c.createdAt', 'DESC')
      .skip((page - 1) * limit)
      .take(limit);

    const [data, total] = await query.getManyAndCount();
    return { data, meta: { total, page, limit } };
  }

  async findOne(id: number) {
    const circulation = await this.circulationRepo.findOne({
      where: { id },
      relations: ['routings', 'routings.assignee', 'correspondence', 'creator'],
      order: { routings: { stepNumber: 'ASC' } },
    });
    if (!circulation) throw new NotFoundException('Circulation not found');
    return circulation;
  }

  // ✅ Logic อัปเดตสถานะและปิดงาน
  async updateRoutingStatus(
    routingId: number,
    dto: UpdateCirculationRoutingDto,
    user: User
  ) {
    const routing = await this.routingRepo.findOne({
      where: { id: routingId },
      relations: ['circulation'],
    });

    if (!routing) throw new NotFoundException('Routing task not found');

    // Check Permission: คนทำต้องเป็นเจ้าของ Task
    if (routing.assignedTo !== user.user_id) {
      throw new ForbiddenException('You are not assigned to this task');
    }

    // Update Routing
    routing.status = dto.status;
    routing.comments = dto.comments;
    routing.completedAt = new Date();
    await this.routingRepo.save(routing);

    // Check: ถ้าทุกคนทำเสร็จแล้ว ให้ปิดใบเวียน (Master)
    const pendingCount = await this.routingRepo.count({
      where: {
        circulationId: routing.circulationId,
        status: 'PENDING', // หรือ status ที่ยังไม่เสร็จ
      },
    });

    if (pendingCount === 0) {
      await this.circulationRepo.update(routing.circulationId, {
        statusCode: 'COMPLETED',
        closedAt: new Date(),
      });
    }

    return routing;
  }
}
