import { Injectable } from '@nestjs/common';
import { DataSource } from 'typeorm';

/** A yard's load/unload capability, resolved for the handling flows. */
export interface YardFacilityInfo {
  yardId: string;
  yardCode: string | null;
  yardLabel: string | null;
  /** The yard can load/unload cargo at all. */
  hasFacility: boolean;
  /** The facility stores cargo — enables the warehouse flow (storage, demurrage). */
  hasWarehouse: boolean;
  /** Containers need a reach stacker/gantry — not every facility has one. */
  handlesContainer: boolean;
  handlesBulk: boolean;
}

/**
 * Which yards can handle cargo, and what kind.
 *
 * A yard is a load/unload point when `yards.has_facility` is set; the matching
 * `yard_facilities` record says what it can actually do — whether it stores cargo
 * (storage/demurrage), and which freight types its equipment can lift. Containers
 * need a reach stacker or gantry, so only Indode, Modjo and Dire Dawa take them;
 * bulk is handled at all five.
 *
 * This is the single resolver the journey and handling flows use, so they can't
 * drift on what a facility is or what it can lift.
 */
@Injectable()
export class YardFacilitiesService {
  constructor(private readonly dataSource: DataSource) {}

  private readonly SELECT = `
    SELECT y.id                AS "yardId",
           y.code              AS "yardCode",
           y.label             AS "yardLabel",
           y.has_facility      AS "hasFacility",
           f.has_warehouse     AS "hasWarehouse",
           f.handles_container AS "handlesContainer",
           f.handles_bulk      AS "handlesBulk"
      FROM freight.yards y
      LEFT JOIN freight.yard_facilities f
        ON f.yard_id = y.id AND f.deleted_at IS NULL AND f.is_active = true`;

  private toInfo(row: {
    yardId: string;
    yardCode: string | null;
    yardLabel: string | null;
    hasFacility: boolean;
    hasWarehouse: boolean | null;
    handlesContainer: boolean | null;
    handlesBulk: boolean | null;
  }): YardFacilityInfo {
    // No facility record means no capability, whatever the flag says.
    const hasFacility = Boolean(row.hasFacility);
    return {
      yardId: row.yardId,
      yardCode: row.yardCode,
      yardLabel: row.yardLabel,
      hasFacility,
      hasWarehouse: hasFacility && Boolean(row.hasWarehouse),
      handlesContainer: hasFacility && Boolean(row.handlesContainer),
      handlesBulk: hasFacility && Boolean(row.handlesBulk),
    };
  }

  /** Resolve a yard's handling capability. Null when the yard doesn't exist. */
  async facilityForYard(yardId: string): Promise<YardFacilityInfo | null> {
    const [row] = await this.dataSource.query(
      `${this.SELECT} WHERE y.id = $1 AND y.deleted_at IS NULL`,
      [yardId],
    );
    return row ? this.toInfo(row) : null;
  }

  /** Every yard that can load/unload, for pickers and the intercity queues. */
  async listFacilityYards(): Promise<YardFacilityInfo[]> {
    const rows = await this.dataSource.query(
      `${this.SELECT}
        WHERE y.deleted_at IS NULL AND y.is_active = true AND y.has_facility = true
        ORDER BY y.display_order ASC, y.label ASC`,
    );
    return rows.map((r: Parameters<typeof this.toInfo>[0]) => this.toInfo(r));
  }

  /**
   * Can this facility lift this cargo? Keeps the freight-type rule in one place
   * so callers can't get it subtly wrong.
   */
  canHandleFreight(
    facility: YardFacilityInfo | null,
    freightType: string | null | undefined,
  ): boolean {
    if (!facility?.hasFacility) return false;
    return String(freightType).toUpperCase() === 'CONTAINER'
      ? facility.handlesContainer
      : facility.handlesBulk;
  }
}
