import { Injectable, NotFoundException } from '@nestjs/common';

import { BookingsRepository } from '../modules/bookings/bookings.repository';
import { Booking } from '../modules/bookings/entities/booking.entity';
import {
  BookingContractSignature,
  ContractSignerRole,
} from '../modules/bookings/entities/booking-contract-signature.entity';
import { ContractPricingScheduleBuilder, PricingSchedule } from './contract-pricing-schedule.builder';
import { ContractTemplateResolver } from './contract-template.resolver';
import { ContractTemplateMeta, getTemplateMeta } from './contract-template.registry';

export interface ContractSignatureView {
  role: ContractSignerRole;
  signerDisplayName: string;
  signedAt: string;
  signatureImageUrl?: string | null;
}

export interface ContractViewModel {
  bookingId: string;
  reference: string;
  status: string;
  templateKey: string;
  template: ContractTemplateMeta;
  contractDate: string;
  contractYear: number;
  client: {
    companyName: string;
    companyAddress: string;
    companyLocation: string;
    phone: string;
    email: string;
    tinNumber: string;
    vatNumber: string;
    fanNumber: string;
    businessLicense: string;
  };
  provider: {
    name: string;
    address: string;
    phone: string;
    email: string;
    tinNumber: string;
  };
  schedule: {
    originLabel: string;
    destinationLabel: string;
    tradeDirection: string;
    freightType: string;
    serviceType: string;
    scheduledDate: string;
    contractType: string;
    cargoDescription: string;
    totalWeightVgm: string;
    equipmentReturn: string;
    hazardousLabel: string;
    firstMilePickupAddress: string;
    lastMileDeliveryAddress: string;
  };
  pricing: PricingSchedule;
  signatures: ContractSignatureView[];
  canSignCustomer: boolean;
  canSignStaff: boolean;
  hasContractDocument: boolean;
  hasCustomerSignature: boolean;
  hasStaffSignature: boolean;
}

@Injectable()
export class ContractViewModelBuilder {
  constructor(
    private readonly bookingsRepository: BookingsRepository,
    private readonly templateResolver: ContractTemplateResolver,
    private readonly pricingBuilder: ContractPricingScheduleBuilder,
  ) {}

  async build(bookingId: string): Promise<{ booking: Booking; view: ContractViewModel }> {
    const booking = await this.bookingsRepository.findByIdWithFiles(bookingId);
    if (!booking) {
      throw new NotFoundException(`Booking ${bookingId} not found`);
    }

    const templateKey =
      booking.contractTemplateKey ?? this.templateResolver.resolve(booking);
    const template = getTemplateMeta(templateKey);
    const pricing = await this.pricingBuilder.build(booking);
    const signatures = await this.loadSignatures(bookingId);

    const hasCustomer = signatures.some((s) => s.role === 'CUSTOMER');
    const hasStaff = signatures.some((s) => s.role === 'STAFF');
    const hasContractFile = Boolean(
      booking.files?.some((f) => f.code === 'contract'),
    );

    const view: ContractViewModel = {
      bookingId: booking.id,
      reference: booking.reference,
      status: booking.status,
      templateKey,
      template,
      contractDate: new Date().toLocaleDateString('en-GB', {
        day: 'numeric',
        month: 'long',
        year: 'numeric',
      }),
      contractYear: new Date().getFullYear(),
      client: {
        companyName: booking.company?.name ?? 'Client',
        companyAddress: this.valueOrDash(booking.company?.address),
        companyLocation: this.valueOrDash(booking.company?.country),
        phone: this.valueOrDash(booking.company?.phone),
        email: this.valueOrDash(booking.company?.email),
        tinNumber: this.valueOrDash(booking.company?.tin),
        vatNumber: this.valueOrDash(booking.company?.vatNumber),
        fanNumber: this.valueOrDash(booking.company?.fanNumber),
        businessLicense: this.valueOrDash(
          booking.company?.companyProfiles?.[0]?.businessLicense,
        ),
      },
      provider: {
        name: 'Ethio-Djibouti Standard Gauge Railway Share Company',
        address: 'Addis Ababa, Ethiopia',
        phone: '+251 11 872 0000',
        email: 'info@edr.gov.et',
        tinNumber: '—',
      },
      schedule: this.buildSchedule(booking),
      pricing,
      signatures,
      canSignCustomer:
        booking.status === 'CONTRACT_READY' && !hasCustomer,
      canSignStaff:
        booking.status === 'SIGNED_CUSTOMER' && hasCustomer && !hasStaff,
      hasContractDocument: hasContractFile,
      hasCustomerSignature: hasCustomer,
      hasStaffSignature: hasStaff,
    };

    return { booking, view };
  }

  private async loadSignatures(bookingId: string): Promise<ContractSignatureView[]> {
    const rows = await this.bookingsRepository.findContractSignatures(bookingId);
    return rows.map((s) => this.toSignatureView(s));
  }

  toSignatureView(row: BookingContractSignature): ContractSignatureView {
    return {
      role: row.signerRole,
      signerDisplayName: row.signerDisplayName,
      signedAt: this.formatDate(row.signedAt),
      signatureImageUrl: row.signatureFile?.url ?? null,
    };
  }

  private buildSchedule(booking: Booking): ContractViewModel['schedule'] {
    const cargoName =
      booking.freightType === 'BULK'
        ? booking.cargoFreeText ||
          booking.cargoType?.cargoTypeName ||
          'Bulk commodity'
        : booking.cargoType?.cargoTypeName || 'Container cargo';
    const totalWeight = Number(booking.cargoTotalWeightVgm || 0);

    return {
      originLabel: this.yardLabel(booking.originYard),
      destinationLabel: this.yardLabel(booking.destinationYard),
      tradeDirection: this.valueOrDash(booking.tradeDirection),
      freightType: this.valueOrDash(booking.freightType),
      serviceType: this.valueOrDash(
        booking.serviceType?.serviceName ?? booking.serviceType?.code,
      ),
      scheduledDate: this.formatDate(booking.scheduledDate),
      contractType: this.valueOrDash(booking.contractType),
      cargoDescription: this.valueOrDash(cargoName),
      totalWeightVgm:
        totalWeight > 0 ? `${totalWeight.toLocaleString()} tons` : '—',
      equipmentReturn: this.valueOrDash(booking.equipmentReturn),
      hazardousLabel: booking.isHazardous ? 'Yes' : 'No',
      firstMilePickupAddress: this.valueOrDash(
        booking.firstMilePickupAddress,
      ),
      lastMileDeliveryAddress: this.valueOrDash(
        booking.lastMileDeliveryAddress,
      ),
    };
  }

  private yardLabel(yard?: { label?: string; code?: string } | null): string {
    return this.valueOrDash(yard?.label ?? yard?.code);
  }

  private formatDate(value?: Date | string | null): string {
    if (!value) return '—';
    const date = value instanceof Date ? value : new Date(value);
    if (Number.isNaN(date.getTime())) return '—';
    return date.toLocaleDateString('en-GB', {
      day: 'numeric',
      month: 'long',
      year: 'numeric',
    });
  }

  private valueOrDash(value?: string | number | null): string {
    if (value === undefined || value === null || value === '') return '—';
    return String(value);
  }
}
