/**
 * Tool Loan Service
 * ERP Construccion - Modulo Assets (MAE-015)
 *
 * Logica de negocio para prestamos de herramientas entre obras/empleados.
 * @gap GAP-002
 */

import { Repository, DataSource, LessThan } from 'typeorm';
import { ToolLoan, LoanStatus } from '../entities/tool-loan.entity';
import { Asset } from '../entities/asset.entity';

// DTOs
export interface CreateToolLoanDto {
  toolId: string;
  employeeId: string;
  employeeName?: string;
  fraccionamientoOrigenId?: string;
  fraccionamientoOrigenName?: string;
  fraccionamientoDestinoId?: string;
  fraccionamientoDestinoName?: string;
  loanDate: Date;
  expectedReturnDate?: Date;
  conditionOut?: string;
  conditionOutPhotos?: string[];
  notes?: string;
}

export interface UpdateToolLoanDto {
  expectedReturnDate?: Date;
  conditionOut?: string;
  conditionOutPhotos?: string[];
  notes?: string;
  metadata?: Record<string, any>;
}

export interface ReturnToolDto {
  actualReturnDate: Date;
  conditionIn?: string;
  conditionInPhotos?: string[];
  status?: 'returned' | 'damaged';
  notes?: string;
}

export interface ToolLoanFilters {
  status?: LoanStatus;
  employeeId?: string;
  toolId?: string;
  fraccionamientoOrigenId?: string;
  fraccionamientoDestinoId?: string;
  overdue?: boolean;
  search?: string;
}

// Re-use shared interfaces
interface PaginationOptions {
  page: number;
  limit: number;
}

interface PaginatedResult<T> {
  data: T[];
  total: number;
  page: number;
  limit: number;
  totalPages: number;
}

export class ToolLoanService {
  private loanRepository: Repository<ToolLoan>;
  private assetRepository: Repository<Asset>;

  constructor(dataSource: DataSource) {
    this.loanRepository = dataSource.getRepository(ToolLoan);
    this.assetRepository = dataSource.getRepository(Asset);
  }

  // ============================================
  // CRUD
  // ============================================

  async create(tenantId: string, dto: CreateToolLoanDto, userId?: string): Promise<ToolLoan> {
    // Verify tool exists and is available
    const tool = await this.assetRepository.findOne({
      where: { id: dto.toolId, tenantId },
    });

    if (!tool) {
      throw new Error('Tool not found');
    }

    if (tool.assetType !== 'tool') {
      throw new Error('Asset is not a tool');
    }

    // Check if tool already has an active loan
    const activeLoan = await this.loanRepository.findOne({
      where: { tenantId, toolId: dto.toolId, status: 'active' },
    });

    if (activeLoan) {
      throw new Error('Tool already has an active loan');
    }

    const loan = this.loanRepository.create({
      tenantId,
      ...dto,
      status: 'active',
      createdBy: userId,
    });

    const savedLoan = await this.loanRepository.save(loan);

    // Update tool status
    await this.assetRepository.update(
      { id: dto.toolId, tenantId },
      { status: 'assigned', currentProjectId: dto.fraccionamientoDestinoId }
    );

    return savedLoan;
  }

  async findById(tenantId: string, id: string): Promise<ToolLoan | null> {
    return this.loanRepository.findOne({
      where: { id, tenantId },
      relations: ['tool'],
    });
  }

  async findAll(
    tenantId: string,
    filters: ToolLoanFilters = {},
    pagination: PaginationOptions = { page: 1, limit: 20 }
  ): Promise<PaginatedResult<ToolLoan>> {
    const queryBuilder = this.loanRepository.createQueryBuilder('loan')
      .leftJoinAndSelect('loan.tool', 'tool')
      .where('loan.tenant_id = :tenantId', { tenantId });

    if (filters.status) {
      queryBuilder.andWhere('loan.status = :status', { status: filters.status });
    }
    if (filters.employeeId) {
      queryBuilder.andWhere('loan.employee_id = :employeeId', { employeeId: filters.employeeId });
    }
    if (filters.toolId) {
      queryBuilder.andWhere('loan.tool_id = :toolId', { toolId: filters.toolId });
    }
    if (filters.fraccionamientoOrigenId) {
      queryBuilder.andWhere('loan.fraccionamiento_origen_id = :origen', { origen: filters.fraccionamientoOrigenId });
    }
    if (filters.fraccionamientoDestinoId) {
      queryBuilder.andWhere('loan.fraccionamiento_destino_id = :destino', { destino: filters.fraccionamientoDestinoId });
    }
    if (filters.overdue) {
      queryBuilder.andWhere('loan.status = :active', { active: 'active' });
      queryBuilder.andWhere('loan.expected_return_date < :today', { today: new Date() });
    }
    if (filters.search) {
      queryBuilder.andWhere(
        '(loan.employee_name ILIKE :search OR tool.name ILIKE :search OR tool.asset_code ILIKE :search)',
        { search: `%${filters.search}%` }
      );
    }

    const skip = (pagination.page - 1) * pagination.limit;

    const [data, total] = await queryBuilder
      .orderBy('loan.loan_date', 'DESC')
      .skip(skip)
      .take(pagination.limit)
      .getManyAndCount();

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

  async update(tenantId: string, id: string, dto: UpdateToolLoanDto, userId?: string): Promise<ToolLoan | null> {
    const loan = await this.findById(tenantId, id);
    if (!loan) return null;

    if (loan.status !== 'active') {
      throw new Error('Cannot update a closed loan');
    }

    Object.assign(loan, dto, { updatedBy: userId });
    return this.loanRepository.save(loan);
  }

  // ============================================
  // LOAN OPERATIONS
  // ============================================

  async returnTool(tenantId: string, id: string, dto: ReturnToolDto, userId?: string): Promise<ToolLoan | null> {
    const loan = await this.findById(tenantId, id);
    if (!loan) return null;

    if (loan.status !== 'active') {
      throw new Error('Loan is not active');
    }

    loan.actualReturnDate = dto.actualReturnDate;
    loan.conditionIn = dto.conditionIn;
    loan.conditionInPhotos = dto.conditionInPhotos;
    loan.status = dto.status || 'returned';
    loan.receivedById = userId;
    loan.notes = dto.notes || loan.notes;
    loan.updatedBy = userId;

    const savedLoan = await this.loanRepository.save(loan);

    // Update tool status back to available
    await this.assetRepository.update(
      { id: loan.toolId, tenantId },
      {
        status: dto.status === 'damaged' ? 'in_maintenance' : 'available',
        currentProjectId: undefined,
      }
    );

    return savedLoan;
  }

  async markAsLost(tenantId: string, id: string, notes?: string, userId?: string): Promise<ToolLoan | null> {
    const loan = await this.findById(tenantId, id);
    if (!loan) return null;

    if (loan.status !== 'active') {
      throw new Error('Loan is not active');
    }

    loan.status = 'lost';
    loan.notes = notes || loan.notes;
    loan.updatedBy = userId;

    const savedLoan = await this.loanRepository.save(loan);

    // Update tool status to inactive
    await this.assetRepository.update(
      { id: loan.toolId, tenantId },
      { status: 'inactive' }
    );

    return savedLoan;
  }

  async approveLoan(tenantId: string, id: string, userId: string): Promise<ToolLoan | null> {
    const loan = await this.findById(tenantId, id);
    if (!loan) return null;

    loan.approvedById = userId;
    loan.approvedAt = new Date();
    loan.updatedBy = userId;

    return this.loanRepository.save(loan);
  }

  // ============================================
  // QUERIES
  // ============================================

  async getActiveLoans(tenantId: string): Promise<ToolLoan[]> {
    return this.loanRepository.find({
      where: { tenantId, status: 'active' },
      relations: ['tool'],
      order: { loanDate: 'DESC' },
    });
  }

  async getOverdueLoans(tenantId: string): Promise<ToolLoan[]> {
    const today = new Date();
    return this.loanRepository.find({
      where: {
        tenantId,
        status: 'active',
        expectedReturnDate: LessThan(today),
      },
      relations: ['tool'],
      order: { expectedReturnDate: 'ASC' },
    });
  }

  async getLoansByEmployee(tenantId: string, employeeId: string): Promise<ToolLoan[]> {
    return this.loanRepository.find({
      where: { tenantId, employeeId },
      relations: ['tool'],
      order: { loanDate: 'DESC' },
    });
  }

  async getLoansByTool(tenantId: string, toolId: string): Promise<ToolLoan[]> {
    return this.loanRepository.find({
      where: { tenantId, toolId },
      order: { loanDate: 'DESC' },
    });
  }

  async getStatistics(tenantId: string): Promise<{
    active: number;
    returned: number;
    overdue: number;
    lost: number;
    damaged: number;
  }> {
    const today = new Date();

    const [active, returned, overdue, lost, damaged] = await Promise.all([
      this.loanRepository.count({ where: { tenantId, status: 'active' } }),
      this.loanRepository.count({ where: { tenantId, status: 'returned' } }),
      this.loanRepository.count({
        where: {
          tenantId,
          status: 'active',
          expectedReturnDate: LessThan(today),
        }
      }),
      this.loanRepository.count({ where: { tenantId, status: 'lost' } }),
      this.loanRepository.count({ where: { tenantId, status: 'damaged' } }),
    ]);

    return { active, returned, overdue, lost, damaged };
  }
}
