import { Injectable } from "@nestjs/common";
import { InjectModel } from "@nestjs/mongoose";
import { Model, Types } from "mongoose";
import { Batch, BatchDocument } from "./schemas/batch.schema";
import { Item, ItemDocument } from "./schemas/item.schema";
import { StockTransaction, StockTransactionDocument } from "./schemas/stock-transaction.schema";
import { Warehouse, WarehouseDocument } from "./schemas/warehouse.schema";

export interface ReportRow {
  id: string;
  date: string;
  type: "inbound" | "outbound";
  warehouse: string | null;
  itemCode: string | null;
  itemName: string | null;
  batchNumber: string | null;
  quantityMt: number;
  approvalStatus: string;
}

export interface ReportFilters {
  warehouseNames?: string[];
  type?: "inbound" | "outbound" | "all";
  fromDate?: string;
  toDate?: string;
}

@Injectable()
export class ReportsService {
  constructor(
    @InjectModel(StockTransaction.name)
    private readonly txModel: Model<StockTransactionDocument>,
    @InjectModel(Warehouse.name)
    private readonly warehouseModel: Model<WarehouseDocument>,
    @InjectModel(Item.name)
    private readonly itemModel: Model<ItemDocument>,
    @InjectModel(Batch.name)
    private readonly batchModel: Model<BatchDocument>,
  ) {}

  async getTransactionReport(filters: ReportFilters): Promise<ReportRow[]> {
    const query: Record<string, unknown> = { approvalStatus: "approved" };

    if (filters.type && filters.type !== "all") {
      query.type = filters.type;
    }

    if (filters.fromDate || filters.toDate) {
      const dateFilter: Record<string, string> = {};
      if (filters.fromDate) dateFilter.$gte = filters.fromDate;
      if (filters.toDate) dateFilter.$lte = filters.toDate;
      query.transactionDate = dateFilter;
    }

    const transactions = await this.txModel
      .find(query)
      .sort({ transactionDate: -1 })
      .lean()
      .exec();

    const rows = await Promise.all(
      transactions.map(async (tx) => {
        const warehouse = await this.warehouseModel.findById(tx.warehouseId).lean().exec();
        const item = await this.itemModel.findById(tx.itemId).lean().exec();
        const batch = await this.batchModel.findById(tx.batchId).lean().exec();

        return {
          id: (tx._id as Types.ObjectId).toString(),
          date: tx.transactionDate,
          type: tx.type as "inbound" | "outbound",
          warehouse: warehouse?.name ?? null,
          itemCode: item?.itemCode ?? null,
          itemName: item?.itemName ?? null,
          batchNumber: batch?.batchNumber ?? null,
          quantityMt: tx.quantityMt,
          approvalStatus: tx.approvalStatus,
        };
      }),
    );

    if (filters.warehouseNames && filters.warehouseNames.length > 0) {
      return rows.filter((r) =>
        r.warehouse &&
        filters.warehouseNames!.some(
          (wn) => wn.toLowerCase() === r.warehouse!.toLowerCase(),
        ),
      );
    }

    return rows;
  }

  async getSummaryByWarehouse(filters: ReportFilters) {
    const rows = await this.getTransactionReport(filters);

    const map = new Map<
      string,
      { warehouse: string; totalInbound: number; totalOutbound: number; net: number }
    >();

    for (const row of rows) {
      const key = row.warehouse ?? "Unknown";
      if (!map.has(key)) {
        map.set(key, { warehouse: key, totalInbound: 0, totalOutbound: 0, net: 0 });
      }
      const entry = map.get(key)!;
      if (row.type === "inbound") {
        entry.totalInbound += row.quantityMt;
      } else {
        entry.totalOutbound += row.quantityMt;
      }
      entry.net = entry.totalInbound - entry.totalOutbound;
    }

    return Array.from(map.values()).sort((a, b) => a.warehouse.localeCompare(b.warehouse));
  }
}
