import { Request, Response } from 'express';
import { StockItem, StockMovement, StockAuditLog, TechnicalDataSheet } from '../lib/compat.js';
import { IAppUser } from '../middleware/authMiddleware.js';
import { notificationService } from '../services/notificationService.js';

interface AuthRequest extends Request {
    appUser?: IAppUser;
}

export const createStockItem = async (req: AuthRequest, res: Response) => {
    try {
        const organizationId = req.appUser?.organizationId;
        const userName = req.appUser?.name || req.appUser?.email || 'Unknown User';
        const {
            dataSheetId,
            rrNumber,
            batchNumber,
            quantity,
            unit,
            expirationDate,
            notes,
            color,
            invoiceNumber,
            receivedBy,
            minStock
        } = req.body;

        if (!dataSheetId || !rrNumber || !batchNumber || quantity === undefined || !unit) {
            return res.status(400).json({ error: 'Campos obrigatórios: DataSheet, RR, Lote, Quantidade, Unidade.' });
        }

        const existing = await StockItem.findOne({ organizationId, rrNumber });
        if (existing) {
            return res.status(400).json({ error: `Já existe um item com o RR ${rrNumber}.` });
        }

        let finalMinStock = Number(minStock) || 0;
        if (finalMinStock === 0) {
            const items = await StockItem.find({ organizationId, dataSheetId, color });
            if (items.length > 0) {
                finalMinStock = items[0].minStock || 0;
            }
        } else {
            if (finalMinStock > 0) {
                await StockItem.updateMany(
                    { organizationId, dataSheetId, color },
                    { minStock: finalMinStock }
                );
            }
        }

        const savedItem = await StockItem.create({
            organizationId,
            createdBy: req.appUser?.id,
            dataSheetId,
            rrNumber,
            batchNumber,
            quantity: Number(quantity),
            unit,
            minStock: finalMinStock,
            expirationDate,
            notes,
            color,
            invoiceNumber,
            receivedBy
        });

        await StockMovement.create({
            organizationId,
            createdBy: req.appUser?.id,
            stockItemId: savedItem.id,
            movementNumber: 1,
            type: 'ENTRY',
            quantity: Number(quantity),
            responsible: userName,
            notes: 'Abertura de Lote / Entrada Inicial'
        });

        if (organizationId) {
            await notificationService.create({
                organizationId,
                title: 'Recebimento de Material',
                message: `Recebido: ${quantity}${unit} de ${savedItem.rrNumber} (Lote: ${batchNumber}).`,
                type: 'info',
                metadata: { stockItemId: savedItem.id, triggerType: 'stock_received' }
            });
            await notificationService.checkLowStock(savedItem.id);
        }

        res.status(201).json(savedItem);
    } catch (error: unknown) {
        console.error('Error creating stock item:', error);
        res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' });
    }
};

export const updateStockItem = async (req: AuthRequest, res: Response) => {
    try {
        const { id } = req.params;
        const organizationId = req.appUser?.organizationId;
        const { quantity, ...otherData } = req.body;

        if (quantity !== undefined) {
            return res.status(400).json({ error: 'Para alterar a quantidade, utilize as funções de Ajuste ou Consumo.' });
        }

        if (otherData.minStock !== undefined) {
            const item = await StockItem.findOne({ id, organizationId });
            if (item) {
                await StockItem.updateMany(
                    { organizationId, dataSheetId: item.dataSheetId, color: item.color },
                    { minStock: otherData.minStock }
                );
            }
        }

        const updated = await StockItem.findOneAndUpdate({ id, organizationId }, otherData);
        if (!updated) return res.status(404).json({ error: 'Item não encontrado.' });

        await notificationService.checkLowStock(id);
        res.json(updated);
    } catch (error: unknown) {
        res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' });
    }
};

export const adjustStock = async (req: AuthRequest, res: Response) => {
    try {
        const { id } = req.params;
        const organizationId = req.appUser?.organizationId;
        const userName = req.appUser?.name || req.appUser?.email || 'Unknown User';
        const { quantityDelta, reason } = req.body;

        if (!reason) return res.status(400).json({ error: 'Motivo é obrigatório para ajustes técnicos.' });
        if (!quantityDelta || isNaN(quantityDelta)) return res.status(400).json({ error: 'Quantidade inválida.' });

        const item = await StockItem.findOne({ id, organizationId });
        if (!item) return res.status(404).json({ error: 'Item não encontrado.' });

        const newQuantity = Number(item.quantity) + Number(quantityDelta);
        if (newQuantity < 0) return res.status(400).json({ error: 'Estoque insuficiente para este ajuste.' });

        await StockItem.findOneAndUpdate({ id }, { quantity: newQuantity });

        const count = await StockMovement.countDocuments({ stockItemId: id });
        
        await StockMovement.create({
            organizationId,
            createdBy: req.appUser?.id,
            stockItemId: id,
            movementNumber: count + 1,
            type: 'ADJUSTMENT',
            quantity: Number(quantityDelta),
            responsible: userName,
            reason
        });

        await notificationService.checkLowStock(id);
        res.json({ ...item, quantity: newQuantity });
    } catch (error: unknown) {
        res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' });
    }
};

export const consumeStock = async (req: AuthRequest, res: Response) => {
    try {
        const { id } = req.params;
        const organizationId = req.appUser?.organizationId;
        const userName = req.appUser?.name || req.appUser?.email || 'Unknown User';
        const { quantityConsumed, requester, date } = req.body;

        if (!requester) return res.status(400).json({ error: 'Solicitante é obrigatório.' });
        if (!quantityConsumed || Number(quantityConsumed) <= 0) return res.status(400).json({ error: 'Quantidade deve ser maior que zero.' });

        const item = await StockItem.findOne({ id, organizationId });
        if (!item) return res.status(404).json({ error: 'Item não encontrado.' });

        if (item.quantity < Number(quantityConsumed)) return res.status(400).json({ error: 'Estoque insuficiente.' });

        const newQuantity = Number(item.quantity) - Number(quantityConsumed);
        await StockItem.findOneAndUpdate({ id }, { quantity: newQuantity });

        const count = await StockMovement.countDocuments({ stockItemId: id });

        await StockMovement.create({
            organizationId,
            createdBy: req.appUser?.id,
            stockItemId: id,
            movementNumber: count + 1,
            type: 'CONSUMPTION',
            quantity: -Number(quantityConsumed),
            responsible: userName,
            requester,
            date: date || new Date()
        });

        await notificationService.checkLowStock(id);
        res.json({ ...item, quantity: newQuantity });
    } catch (error: unknown) {
        res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' });
    }
};

export const deleteStockItem = async (req: AuthRequest, res: Response) => {
    try {
        const { id } = req.params;
        const organizationId = req.appUser?.organizationId;

        const deleted = await StockItem.findOneAndDelete({ id, organizationId });
        if (!deleted) return res.status(404).json({ error: 'Item não encontrado.' });

        await StockMovement.deleteMany({ stockItemId: id });
        await StockAuditLog.deleteMany({ stockItemId: id });

        res.status(204).send();
    } catch (error: unknown) {
        res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' });
    }
};

export const getStockItems = async (req: AuthRequest, res: Response) => {
    try {
        const organizationId = req.appUser?.organizationId;
        const { dataSheetId } = req.query;

        const query: any = { organizationId };
        if (dataSheetId) query.dataSheetId = dataSheetId;

        const items = await StockItem.find(query);

        // Manual population
        const itemsWithDetails = await Promise.all(items.map(async (item: any) => {
            if (item.dataSheetId) {
                const ds = await TechnicalDataSheet.findById(item.dataSheetId);
                return { ...item, dataSheetId: ds || item.dataSheetId };
            }
            return item;
        }));

        res.json(itemsWithDetails);
    } catch (error: unknown) {
        res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' });
    }
};

export const getStockItemById = async (req: AuthRequest, res: Response) => {
    try {
        const { id } = req.params;
        const organizationId = req.appUser?.organizationId;

        const item = await StockItem.findOne({ id, organizationId });
        if (!item) return res.status(404).json({ error: 'Item não encontrado.' });

        if (item.dataSheetId) {
            const ds = await TechnicalDataSheet.findById(item.dataSheetId);
            item.dataSheetId = ds || item.dataSheetId;
        }

        res.json(item);
    } catch (error: unknown) {
        res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' });
    }
};

export const getStockMovements = async (req: AuthRequest, res: Response) => {
    try {
        const { id } = req.params;
        const organizationId = req.appUser?.organizationId;
        const movements = await StockMovement.find({ stockItemId: id, organizationId });
        res.json(movements);
    } catch (error: unknown) {
        res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' });
    }
};

export const updateStockMovement = async (req: AuthRequest, res: Response) => {
    try {
        const { id } = req.params;
        const organizationId = req.appUser?.organizationId;
        const userName = req.appUser?.name || req.appUser?.email || 'Unknown User';
        const userId = req.appUser?.id || 'system';
        const isAdmin = req.appUser?.role === 'admin' || req.appUser?.organizationRole === 'admin';

        if (!isAdmin) return res.status(403).json({ error: 'No admin permissions.' });

        const { date, quantity, notes } = req.body;
        const movement = await StockMovement.findOne({ id, organizationId });
        if (!movement) return res.status(404).json({ error: 'Movement not found.' });

        const item = await StockItem.findOne({ id: movement.stockItemId, organizationId });
        if (!item) return res.status(404).json({ error: 'Stock item not found.' });

        const oldQuantity = Number(movement.quantity);
        const quantityDiff = Number(quantity) - oldQuantity;

        const newStockLevel = Number(item.quantity) + quantityDiff;
        if (newStockLevel < 0) return res.status(400).json({ error: 'Negative stock results.' });

        await StockItem.findOneAndUpdate({ id: item.id }, { quantity: newStockLevel });

        await StockAuditLog.create({
            organizationId,
            stockItemId: item.id,
            movementId: movement.id,
            movementNumber: movement.movementNumber,
            userId,
            userName,
            action: 'UPDATE',
            details: `Update: ${oldQuantity} -> ${quantity}`,
            oldValues: { date: movement.date, quantity: movement.quantity, notes: movement.notes },
            newValues: { date, quantity, notes }
        });

        const updated = await StockMovement.findOneAndUpdate({ id }, { quantity, date, notes });
        res.json(updated);
    } catch (error: unknown) {
        res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' });
    }
};

export const deleteStockMovement = async (req: AuthRequest, res: Response) => {
    try {
        const { id } = req.params;
        const organizationId = req.appUser?.organizationId;
        const userName = req.appUser?.name || req.appUser?.email || 'Unknown User';
        const userId = req.appUser?.id || 'system';
        const isAdmin = req.appUser?.role === 'admin' || req.appUser?.organizationRole === 'admin';

        if (!isAdmin) return res.status(403).json({ error: 'No admin permissions.' });

        const movement = await StockMovement.findOne({ id, organizationId });
        if (!movement) return res.status(404).json({ error: 'Not found.' });

        const item = await StockItem.findOne({ id: movement.stockItemId, organizationId });
        if (!item) return res.status(404).json({ error: 'Item associate not found.' });

        const newStockLevel = Number(item.quantity) - Number(movement.quantity);
        if (newStockLevel < 0) return res.status(400).json({ error: 'Negative stock results.' });

        await StockItem.findOneAndUpdate({ id: item.id }, { quantity: newStockLevel });

        await StockAuditLog.create({
            organizationId,
            stockItemId: item.id,
            movementId: movement.id,
            movementNumber: movement.movementNumber,
            userId,
            userName,
            action: 'DELETE',
            details: `Exclusão: Qtd ${movement.quantity}`,
            oldValues: movement
        });

        await StockMovement.findOneAndDelete({ id });
        res.status(204).send();
    } catch (error: unknown) {
        res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' });
    }
};

export const getStockAuditLogs = async (req: AuthRequest, res: Response) => {
    try {
        const { id } = req.params;
        const organizationId = req.appUser?.organizationId;
        const logs = await StockAuditLog.find({ stockItemId: id, organizationId });
        res.json(logs);
    } catch (error: unknown) {
        res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' });
    }
};
