import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, LessThan } from 'typeorm';
import { Material } from '../entities/material.entity';
import { MaterialRequirement } from '../entities/material-requirement.entity';
import { OrderItem } from '../entities/order-item.entity';
import { CreateMaterialDto } from '../dto/create-material.dto';
import { UpdateMaterialDto } from '../dto/update-material.dto';
import * as XLSX from 'xlsx';
import { BadRequestException } from '@nestjs/common';

@Injectable()
export class InventoryService {
  constructor(
    @InjectRepository(Material)
    private materialRepository: Repository<Material>,
    @InjectRepository(MaterialRequirement)
    private materialRequirementRepository: Repository<MaterialRequirement>,
  ) {}

  async findAll(): Promise<Material[]> {
    return this.materialRepository.find({
      relations: ['requirements'],
      order: {
        updatedAt: 'DESC',
      },
    });
  }

  async findOne(id: string): Promise<Material> {
    return this.materialRepository.findOne({
      where: { id },
      relations: ['requirements'],
    });
  }

  async createMaterial(createDto: CreateMaterialDto): Promise<Material> {
    // Handle supplier as string (ID) or object
    let supplier = null;
    if (createDto.supplier) {
      if (typeof createDto.supplier === 'string') {
        supplier = { id: createDto.supplier };
      } else if (typeof createDto.supplier === 'object' && createDto.supplier.id) {
        supplier = { id: createDto.supplier.id };
      }
    }
    const material = this.materialRepository.create({ ...createDto, supplier });
    return this.materialRepository.save(material);
  }

  async update(id: string, updateDto: UpdateMaterialDto): Promise<Material> {
    // Handle supplier as string (ID) or object
    let supplier = null;
    if (updateDto.supplier) {
      if (typeof updateDto.supplier === 'string') {
        supplier = { id: updateDto.supplier };
      } else if (typeof updateDto.supplier === 'object' && updateDto.supplier.id) {
        supplier = { id: updateDto.supplier.id };
      }
    }
    await this.materialRepository.update(id, { ...updateDto, supplier });
    return this.findOne(id);
  }

  async remove(id: string): Promise<void> {
    await this.materialRepository.delete(id);
  }

  async checkAvailability(materialId: string, quantity: number): Promise<boolean> {
    const material = await this.findOne(materialId);
    return material.stockQuantity >= quantity;
  }

  async allocateMaterial(
    materialId: string,
    quantity: number,
    orderId: string,
    orderItemId: string,
  ): Promise<void> {
    // Create requirement
    const requirement = this.materialRequirementRepository.create({
      materialId,
      orderId,
      orderItemId,
      quantity,
    });
    await this.materialRequirementRepository.save(requirement);

    // Update material stock
    const material = await this.findOne(materialId);
    material.stockQuantity -= quantity;
    await this.materialRepository.save(material);
  }

  async getLowStockMaterials(threshold: number = 10): Promise<Material[]> {
    return this.materialRepository.find({
      where: {
        stockQuantity: LessThan(threshold),
      },
      relations: ['requirements'],
    });
  }

  async getMaterialUsageByPeriod(
    materialId: string,
    startDate: Date,
    endDate: Date,
  ): Promise<number> {
    return this.materialRequirementRepository
      .createQueryBuilder('requirement')
      .where('requirement.materialId = :materialId', { materialId })
      .andWhere('requirement.createdAt BETWEEN :startDate AND :endDate', {
        startDate,
        endDate,
      })
      .getCount();
  }

  // Additional methods for controller compatibility
  async getMaterials(query?: any): Promise<Material[]> {
    return this.findAll();
  }

  async getMaterial(id: string): Promise<Material> {
    return this.findOne(id);
  }

  async updateMaterial(id: string, updateDto: UpdateMaterialDto): Promise<Material> {
    return this.update(id, updateDto);
  }

  async deleteMaterial(id: string): Promise<void> {
    return this.remove(id);
  }

  async getSuppliers(query?: any): Promise<any[]> {
    // This would need a SupplierService or repository
    return [];
  }

  async getSupplier(id: string): Promise<any> {
    // This would need a SupplierService or repository
    return null;
  }

  async createSupplier(createSupplierDto: any): Promise<any> {
    // This would need a SupplierService or repository
    return null;
  }

  async updateSupplier(id: string, updateSupplierDto: any): Promise<any> {
    // This would need a SupplierService or repository
    return null;
  }

  async deleteSupplier(id: string): Promise<void> {
    // This would need a SupplierService or repository
  }

  async getStockMovements(query?: any): Promise<any[]> {
    // This would need a StockMovement entity/service
    return [];
  }

  async createStockMovement(body: any): Promise<any> {
    // This would need a StockMovement entity/service
    return null;
  }

  async getLowStockItems(): Promise<Material[]> {
    return this.getLowStockMaterials();
  }

  async getAnalytics(): Promise<any> {
    // This would need analytics implementation
    return {
      totalMaterials: await this.materialRepository.count(),
      lowStockItems: await this.getLowStockMaterials().then(items => items.length),
    };
  }

  async importMaterialsFromExcel(file: Express.Multer.File): Promise<{ imported: number; errors: any[] }> {
    try {
      const workbook = XLSX.read(file.buffer, { type: 'buffer' });
      const sheetName = workbook.SheetNames[0];
      const worksheet = workbook.Sheets[sheetName];
      const json: any[] = XLSX.utils.sheet_to_json(worksheet, { defval: '' });
      let imported = 0;
      const errors = [];
      for (const row of json) {
        try {
          // Map Excel columns to DTO fields
          const createDto: any = {
            name: row['Name'] || row['name'],
            description: row['Description'] || row['description'],
            type: row['Type'] || row['type'],
            unit: row['Unit'] || row['unit'],
            stockQuantity: Number(row['Stock Quantity'] || row['stockQuantity'] || row['Stock'] || 0),
            reorderLevel: Number(row['Reorder Level'] || row['reorderLevel'] || 0),
            unitPrice: Number(row['Unit Price'] || row['unitPrice'] || 0),
            supplier: row['Supplier'] || row['supplier'],
            location: row['Location'] || row['location'],
            minStock: Number(row['Min Stock'] || row['minStock'] || 0),
            maxStock: Number(row['Max Stock'] || row['maxStock'] || 0),
          };
          if (!createDto.name) throw new Error('Missing name');
          await this.createMaterial(createDto);
          imported++;
        } catch (err) {
          errors.push({ row, error: err.message });
        }
      }
      return { imported, errors };
    } catch (err) {
      throw new BadRequestException('Failed to parse Excel/CSV file: ' + err.message);
    }
  }
}
