import { BadRequestException, Injectable } from '@nestjs/common';
import { ProcurementRepository } from './procurement.repository';
import { Vendor } from './entities/vendor.entity';
import { AcquisitionType, AssetAcquisition } from './entities/asset-acquisition.entity';
import { AssetDisposal } from './entities/asset-disposal.entity';
import {
  CreateVendorDto,
  UpdateVendorDto,
  CreateAcquisitionDto,
  UpdateAcquisitionDto,
  CreateDisposalDto,
} from './dto/procurement.dto';

export interface DepreciationResult {
  method: 'STRAIGHT_LINE';
  cost: number;
  salvageValue: number;
  usefulLifeMonths: number;
  monthsElapsed: number;
  monthlyDepreciation: number;
  bookValue: number;
}

export interface LifecycleResult {
  vehicleId: string;
  acquisition: AssetAcquisition | null;
  disposal: AssetDisposal | null;
  depreciation: DepreciationResult | null;
}

@Injectable()
export class ProcurementService {
  constructor(private readonly procurementRepository: ProcurementRepository) {}

  // ---- Vendors ----
  async createVendor(dto: CreateVendorDto): Promise<Vendor> {
    return this.procurementRepository.createVendor(dto);
  }

  async listVendors(): Promise<Vendor[]> {
    return this.procurementRepository.findVendors();
  }

  async updateVendor(id: string, dto: UpdateVendorDto): Promise<Vendor | null> {
    return this.procurementRepository.updateVendor(id, dto);
  }

  async deleteVendor(id: string): Promise<{ success: boolean }> {
    await this.procurementRepository.softDeleteVendor(id);
    return { success: true };
  }

  // ---- Acquisitions ----
  /** Lease terms only make sense on LEASE / RENTAL — a PURCHASE must not carry them. */
  private assertLeaseFieldsValid(dto: {
    acquisitionType?: string;
    leaseStart?: string;
    leaseEnd?: string;
    monthlyPayment?: number;
  }): void {
    if (dto.acquisitionType !== AcquisitionType.PURCHASE) return;
    if (dto.leaseStart || dto.leaseEnd || dto.monthlyPayment != null) {
      throw new BadRequestException(
        'Lease start/end and monthly payment are not valid for a PURCHASE acquisition',
      );
    }
  }

  async createAcquisition(dto: CreateAcquisitionDto): Promise<AssetAcquisition> {
    this.assertLeaseFieldsValid(dto);
    return this.procurementRepository.createAcquisition(dto);
  }

  async listAcquisitions(vehicleId?: string): Promise<AssetAcquisition[]> {
    return this.procurementRepository.findAcquisitions(vehicleId);
  }

  async getAcquisition(id: string): Promise<AssetAcquisition | null> {
    return this.procurementRepository.findAcquisitionById(id);
  }

  async updateAcquisition(id: string, dto: UpdateAcquisitionDto): Promise<AssetAcquisition | null> {
    // Validate against the resulting record, not just the patch — switching an
    // acquisition to PURCHASE must also shed any stored lease terms.
    const existing = await this.procurementRepository.findAcquisitionById(id);
    if (existing) {
      const next = { ...existing, ...dto };
      if (next.acquisitionType === AcquisitionType.PURCHASE) {
        this.assertLeaseFieldsValid({
          acquisitionType: next.acquisitionType,
          leaseStart: dto.leaseStart,
          leaseEnd: dto.leaseEnd,
          monthlyPayment: dto.monthlyPayment,
        });
      }
    }
    return this.procurementRepository.updateAcquisition(id, dto);
  }

  async deleteAcquisition(id: string): Promise<{ success: boolean }> {
    await this.procurementRepository.softDeleteAcquisition(id);
    return { success: true };
  }

  // ---- Disposals ----
  async createDisposal(dto: CreateDisposalDto): Promise<AssetDisposal> {
    return this.procurementRepository.createDisposal(dto);
  }

  async listDisposals(): Promise<AssetDisposal[]> {
    return this.procurementRepository.findDisposals();
  }

  async deleteDisposal(id: string): Promise<{ success: boolean }> {
    await this.procurementRepository.softDeleteDisposal(id);
    return { success: true };
  }

  // ---- Lifecycle ----
  async lifecycle(vehicleId: string): Promise<LifecycleResult> {
    const acquisition = await this.procurementRepository.findLatestAcquisitionByVehicle(vehicleId);
    const disposal = await this.procurementRepository.findLatestDisposalByVehicle(vehicleId);

    return {
      vehicleId,
      acquisition,
      disposal,
      depreciation: this.computeStraightLineDepreciation(acquisition),
    };
  }

  /**
   * Straight-line depreciation. Requires a cost and a positive useful life.
   * monthlyDep = (cost - salvageValue) / usefulLifeMonths
   * bookValue  = cost - monthlyDep * monthsElapsedSinceAcquisition, floored at salvageValue.
   */
  private computeStraightLineDepreciation(
    acquisition: AssetAcquisition | null,
  ): DepreciationResult | null {
    if (!acquisition) return null;

    const cost = acquisition.cost != null ? Number(acquisition.cost) : null;
    const usefulLifeMonths =
      acquisition.usefulLifeMonths != null ? Number(acquisition.usefulLifeMonths) : null;

    if (cost == null || usefulLifeMonths == null || usefulLifeMonths <= 0) {
      return null;
    }

    const salvageValue = acquisition.salvageValue != null ? Number(acquisition.salvageValue) : 0;
    const monthlyDepreciation = (cost - salvageValue) / usefulLifeMonths;

    const acquiredAt = new Date(acquisition.acquisitionDate);
    const now = new Date();
    const monthsElapsed = Math.max(
      0,
      (now.getFullYear() - acquiredAt.getFullYear()) * 12 +
        (now.getMonth() - acquiredAt.getMonth()),
    );

    const bookValue = Math.max(cost - monthlyDepreciation * monthsElapsed, salvageValue);

    return {
      method: 'STRAIGHT_LINE',
      cost,
      salvageValue,
      usefulLifeMonths,
      monthsElapsed,
      monthlyDepreciation,
      bookValue,
    };
  }
}
