const Asset = require('../models/Asset');
const AssetAssignment = require('../models/AssetAssignment');
const AuditLog = require('../models/AuditLog');
const { AppError } = require('../middleware/errorHandler');

const VALID_TYPES = ['Notebook', 'Monitor', 'Headset', 'Other', 'Maschine', 'Werkzeug', 'Sonstiges'];
const VALID_STATUSES = ['verfuegbar', 'zugewiesen', 'inaktiv', 'beschaedigt', 'bestellt'];

class AssetService {
    /**
     * Get all assets
     */
    static getAllAssets(departments = null) {
        return Asset.getAll({ departments });
    }

    /**
     * Get asset by ID
     */
    static getAssetById(id) {
        const asset = Asset.getById(id);

        if (!asset) {
            throw new AppError('Asset nicht gefunden', 404);
        }

        return asset;
    }

    /**
     * Get asset by serial number
     */
    static getAssetBySerial(serialNumber) {
        const asset = Asset.getBySerialNumber(serialNumber);

        if (!asset) {
            throw new AppError('Asset nicht gefunden', 404);
        }

        return asset;
    }

    /**
     * Get assets by status
     */
    static getAssetsByStatus(status) {
        // Validate status
        if (!VALID_STATUSES.includes(status)) {
            throw new AppError(`Ungültiger Status. Muss einer sein von: ${VALID_STATUSES.join(', ')}`, 400);
        }

        return Asset.getByStatus(status);
    }

    /**
     * Get assets by type
     */
    static getAssetsByType(type) {
        // Validate type
        if (!VALID_TYPES.includes(type)) {
            throw new AppError(`Ungültiger Typ. Muss einer sein von: ${VALID_TYPES.join(', ')}`, 400);
        }

        return Asset.getByType(type);
    }

    /**
     * Create new asset
     */
    static createAsset(assetData, createdByUserId, ipAddress) {
        // Validate required fields
        if (!assetData.name || !assetData.type || !assetData.status) {
            throw new AppError('Fehlende Pflichtfelder (name, type, status)', 400);
        }

        // Auto-generate serial number if not provided
        if (!assetData.serial_number) {
            assetData.serial_number = `AUTO-${Date.now()}-${Math.random().toString(36).substring(2, 7).toUpperCase()}`;
        }

        // Validate type
        if (!VALID_TYPES.includes(assetData.type)) {
            throw new AppError(`Ungültiger Typ. Muss einer sein von: ${VALID_TYPES.join(', ')}`, 400);
        }

        // Validate status
        if (!VALID_STATUSES.includes(assetData.status)) {
            throw new AppError(`Ungültiger Status. Muss einer sein von: ${VALID_STATUSES.join(', ')}`, 400);
        }

        // Check if serial number already exists
        const existingAsset = Asset.getBySerialNumber(assetData.serial_number);
        if (existingAsset) {
            throw new AppError('Seriennummer existiert bereits', 409);
        }

        // Auto-calculate next_maintenance_date from last_maintenance_date + interval
        if (assetData.last_maintenance_date && assetData.maintenance_interval_months && !assetData.next_maintenance_date) {
            const last = new Date(assetData.last_maintenance_date);
            last.setMonth(last.getMonth() + parseInt(assetData.maintenance_interval_months));
            assetData.next_maintenance_date = last.toISOString().split('T')[0];
        }

        // Auto-generate inventory number if not provided
        const { getDatabase } = require('../config/database');
        const db = getDatabase();
        if (!assetData.inventory_number) {
            const last = db.prepare(`SELECT inventory_number FROM assets WHERE inventory_number LIKE 'ANL-%' ORDER BY inventory_number DESC LIMIT 1`).get();
            const nextNum = last ? parseInt(last.inventory_number.replace('ANL-', '')) + 1 : 1;
            assetData.inventory_number = `ANL-${String(nextNum).padStart(4, '0')}`;
        } else {
            // Check for duplicate inventory number
            const existing = db.prepare(`SELECT id FROM assets WHERE inventory_number = ?`).get(assetData.inventory_number);
            if (existing) {
                throw new AppError('Inventarnummer existiert bereits', 409);
            }
        }

        // Create asset
        const newAsset = Asset.create({
            name: assetData.name,
            type: assetData.type,
            serial_number: assetData.serial_number,
            model: assetData.model,
            status: assetData.status,
            purchase_date: assetData.purchase_date,
            description: assetData.description,
            teamviewer_id: assetData.teamviewer_id,
            last_maintenance_date: assetData.last_maintenance_date,
            next_maintenance_date: assetData.next_maintenance_date,
            maintenance_interval_months: assetData.maintenance_interval_months,
            maintenance_notes: assetData.maintenance_notes,
            department: assetData.department || 'IT',
            inventory_number: assetData.inventory_number,
            purchase_price: assetData.purchase_price,
            useful_life_years: assetData.useful_life_years,
            residual_value: assetData.residual_value,
            assigned_to_user_id: assetData.assigned_to_user_id,
            created_by_user_id: createdByUserId
        });

        // Log action
        AuditLog.logUserAction(
            createdByUserId,
            'CREATE_ASSET',
            'asset',
            newAsset.id,
            null,
            { name: newAsset.name, type: newAsset.type, serial_number: newAsset.serial_number },
            ipAddress
        );

        return newAsset;
    }

    /**
     * Update asset
     */
    static updateAsset(id, assetData, updatedByUserId, ipAddress) {
        // Get existing asset
        const existingAsset = Asset.getById(id);
        if (!existingAsset) {
            throw new AppError('Asset nicht gefunden', 404);
        }

        // Validate type if being changed
        if (assetData.type && !VALID_TYPES.includes(assetData.type)) {
            throw new AppError(`Ungültiger Typ. Muss einer sein von: ${VALID_TYPES.join(', ')}`, 400);
        }

        // Validate status if being changed
        if (assetData.status && !VALID_STATUSES.includes(assetData.status)) {
            throw new AppError(`Ungültiger Status. Muss einer sein von: ${VALID_STATUSES.join(', ')}`, 400);
        }

        // Check if serial number is being changed and already exists
        if (assetData.serial_number && assetData.serial_number !== existingAsset.serial_number) {
            const assetWithSerial = Asset.getBySerialNumber(assetData.serial_number);
            if (assetWithSerial) {
                throw new AppError('Seriennummer existiert bereits', 409);
            }
        }

        // Auto-calculate next_maintenance_date from last_maintenance_date + interval
        if (assetData.last_maintenance_date && assetData.maintenance_interval_months && !assetData.next_maintenance_date) {
            const last = new Date(assetData.last_maintenance_date);
            last.setMonth(last.getMonth() + parseInt(assetData.maintenance_interval_months));
            assetData.next_maintenance_date = last.toISOString().split('T')[0];
        }

        // Update asset
        const updatedAsset = Asset.update(id, assetData, updatedByUserId);

        // Log action
        AuditLog.logUserAction(
            updatedByUserId,
            'UPDATE_ASSET',
            'asset',
            id,
            {
                name: existingAsset.name,
                type: existingAsset.type,
                serial_number: existingAsset.serial_number,
                status: existingAsset.status
            },
            {
                name: updatedAsset.name,
                type: updatedAsset.type,
                serial_number: updatedAsset.serial_number,
                status: updatedAsset.status
            },
            ipAddress
        );

        return updatedAsset;
    }

    /**
     * Delete asset
     */
    static deleteAsset(id, deletedByUserId, ipAddress) {
        // Get existing asset
        const existingAsset = Asset.getById(id);
        if (!existingAsset) {
            throw new AppError('Asset nicht gefunden', 404);
        }

        // Check if asset is currently assigned
        const currentAssignment = AssetAssignment.getCurrentAssignment(id);
        if (currentAssignment) {
            throw new AppError('Asset kann nicht gelöscht werden, da es derzeit zugewiesen ist', 400);
        }

        // Delete asset
        const deleted = Asset.delete(id);

        if (deleted) {
            // Log action
            AuditLog.logUserAction(
                deletedByUserId,
                'DELETE_ASSET',
                'asset',
                id,
                {
                    name: existingAsset.name,
                    type: existingAsset.type,
                    serial_number: existingAsset.serial_number
                },
                null,
                ipAddress
            );
        }

        return deleted;
    }

    /**
     * Assign asset to user
     */
    static assignAsset(assetId, userId, assignedByUserId, notes, ipAddress) {
        // Get asset
        const asset = Asset.getById(assetId);
        if (!asset) {
            throw new AppError('Asset nicht gefunden', 404);
        }

        // Check if asset is already assigned
        const currentAssignment = AssetAssignment.getCurrentAssignment(assetId);
        if (currentAssignment) {
            throw new AppError('Asset ist bereits zugewiesen', 400);
        }

        // Check if asset is available
        if (asset.status !== 'verfuegbar') {
            throw new AppError('Asset ist nicht verfügbar', 400);
        }

        // Create assignment
        const assignment = AssetAssignment.create({
            asset_id: assetId,
            user_id: userId,
            assigned_by_user_id: assignedByUserId,
            notes: notes
        });

        // Update asset status and assigned_to_user_id
        Asset.update(assetId, {
            status: 'zugewiesen',
            assigned_to_user_id: userId
        }, assignedByUserId);

        // Log action
        AuditLog.logUserAction(
            assignedByUserId,
            'ASSIGN_ASSET',
            'asset',
            assetId,
            { status: 'verfuegbar', assigned_to_user_id: null },
            { status: 'zugewiesen', assigned_to_user_id: userId },
            ipAddress
        );

        return assignment;
    }

    /**
     * Unassign asset from user
     */
    static unassignAsset(assetId, unassignedByUserId, newStatus, ipAddress) {
        // Get asset
        const asset = Asset.getById(assetId);
        if (!asset) {
            throw new AppError('Asset nicht gefunden', 404);
        }

        // Get current assignment
        const currentAssignment = AssetAssignment.getCurrentAssignment(assetId);
        if (!currentAssignment) {
            throw new AppError('Asset ist nicht zugewiesen', 400);
        }

        // Validate new status
        const validUnassignStatuses = ['verfuegbar', 'inaktiv', 'beschaedigt'];
        if (!validUnassignStatuses.includes(newStatus)) {
            throw new AppError(`Ungültiger Status. Muss einer sein von: ${validUnassignStatuses.join(', ')}`, 400);
        }

        // Mark assignment as returned
        AssetAssignment.markAsReturned(currentAssignment.id);

        // Update asset status and remove assigned_to_user_id
        Asset.update(assetId, {
            status: newStatus,
            assigned_to_user_id: null
        }, unassignedByUserId);

        // Log action
        AuditLog.logUserAction(
            unassignedByUserId,
            'UNASSIGN_ASSET',
            'asset',
            assetId,
            { status: 'zugewiesen', assigned_to_user_id: asset.assigned_to_user_id },
            { status: newStatus, assigned_to_user_id: null },
            ipAddress
        );

        return Asset.getById(assetId);
    }

    /**
     * Get assignment history for an asset
     */
    static getAssetAssignmentHistory(assetId) {
        // Check if asset exists
        const asset = Asset.getById(assetId);
        if (!asset) {
            throw new AppError('Asset nicht gefunden', 404);
        }

        return AssetAssignment.getByAssetId(assetId);
    }

    /**
     * Get statistics
     */
    static getStatistics(departments = null) {
        return Asset.getStatistics({ departments });
    }
}

module.exports = AssetService;
