const nocodbService = require('../services/nocodb');
const logger = require('../utils/logger');
const config = require('../config');
const spatialUtils = require('../utils/spatial');

class CutsController {
    /**
     * Get all cuts - filter by public visibility for non-admins
     */
    async getAll(req, res) {
        try {
            // Check if cuts table is configured
            if (!config.nocodb.cutsSheetId) {
                // Return empty list if cuts table is not configured
                return res.json({ list: [] });
            }
            
            const { isAdmin } = req.user || {};
            
            // For NocoDB v2 API, we need to get all records and filter in memory
            // since the where clause syntax may be different
            // Use paginated method to get ALL records (not just the default 25 limit)
            const response = await nocodbService.getAllPaginated(
                config.nocodb.cutsSheetId
            );
            
            // Ensure response has list property
            if (!response || !response.list) {
                return res.json({ list: [] });
            }
            
            // Filter results based on user permissions
            if (!isAdmin) {
                response.list = response.list.filter(cut => {
                    const isPublic = cut.is_public || cut.Is_public || cut['Public Visibility'];
                    return isPublic === true || isPublic === 1 || isPublic === '1';
                });
            }
            
            logger.info(`Retrieved ${response.list?.length || 0} cuts for ${isAdmin ? 'admin' : 'user'}`);
            res.json(response);
        } catch (error) {
            logger.error('Error fetching cuts:', error);
            // Log more details about the error
            if (error.response) {
                logger.error('Error response:', error.response.data);
                logger.error('Error status:', error.response.status);
            }
            res.status(500).json({ 
                error: 'Failed to fetch cuts',
                details: error.message 
            });
        }
    }
    
    /**
     * Get single cut by ID
     */
    async getById(req, res) {
        try {
            const { id } = req.params;
            const { isAdmin } = req.user || {};
            
            const response = await nocodbService.getById(
                config.nocodb.cutsSheetId,
                id
            );
            
            if (!response) {
                return res.status(404).json({ error: 'Cut not found' });
            }
            
            // Non-admins can only access public cuts
            if (!isAdmin) {
                const isPublic = response.is_public || response.Is_public || response['Public Visibility'];
                if (!(isPublic === true || isPublic === 1 || isPublic === '1')) {
                    return res.status(403).json({ error: 'Access denied' });
                }
            }
            
            logger.info(`Retrieved cut: ${response.name} (ID: ${id})`);
            res.json(response);
        } catch (error) {
            logger.error('Error fetching cut:', error);
            res.status(500).json({ 
                error: 'Failed to fetch cut',
                details: error.message 
            });
        }
    }
    
    /**
     * Create new cut - admin only
     */
    async create(req, res) {
        try {
            const { isAdmin, email } = req.user || {};
            
            if (!isAdmin) {
                return res.status(403).json({ error: 'Admin access required' });
            }
            
            const { 
                name, 
                description, 
                color = '#3388ff', 
                opacity = 0.3, 
                category, 
                is_public = false, 
                is_official = false, 
                geojson,
                bounds,
                assigned_to 
            } = req.body;
            
            // Validate required fields
            if (!name || !geojson) {
                return res.status(400).json({ 
                    error: 'Name and geojson are required' 
                });
            }
            
            // Validate GeoJSON
            try {
                const parsedGeoJSON = JSON.parse(geojson);
                if (parsedGeoJSON.type !== 'Polygon' && parsedGeoJSON.type !== 'MultiPolygon') {
                    return res.status(400).json({ 
                        error: 'GeoJSON must be a Polygon or MultiPolygon' 
                    });
                }
            } catch (parseError) {
                return res.status(400).json({ 
                    error: 'Invalid GeoJSON format' 
                });
            }
            
            // Validate opacity range
            if (opacity < 0 || opacity > 1) {
                return res.status(400).json({ 
                    error: 'Opacity must be between 0 and 1' 
                });
            }
            
            const cutData = {
                name,
                description,
                color,
                opacity,
                category,
                is_public,
                is_official,
                geojson,
                bounds,
                assigned_to,
                created_by: email,
            };
            
            const response = await nocodbService.create(
                config.nocodb.cutsSheetId,
                cutData
            );
            
            logger.info(`Created cut: ${name} by ${email}`);
            res.status(201).json(response);
        } catch (error) {
            logger.error('Error creating cut:', error);
            res.status(500).json({ 
                error: 'Failed to create cut',
                details: error.message 
            });
        }
    }
    
    /**
     * Update cut - admin only
     */
    async update(req, res) {
        try {
            const { isAdmin, email } = req.user || {};
            const { id } = req.params;
            
            if (!isAdmin) {
                return res.status(403).json({ error: 'Admin access required' });
            }
            
            // Check if cut exists
            const existingCut = await nocodbService.getById(
                config.nocodb.cutsSheetId,
                id
            );
            
            if (!existingCut) {
                return res.status(404).json({ error: 'Cut not found' });
            }
            
            const { 
                name, 
                description, 
                color, 
                opacity, 
                category, 
                is_public, 
                is_official, 
                geojson,
                bounds,
                assigned_to 
            } = req.body;
            
            // Validate GeoJSON if provided
            if (geojson) {
                try {
                    const parsedGeoJSON = JSON.parse(geojson);
                    if (parsedGeoJSON.type !== 'Polygon' && parsedGeoJSON.type !== 'MultiPolygon') {
                        return res.status(400).json({ 
                            error: 'GeoJSON must be a Polygon or MultiPolygon' 
                        });
                    }
                } catch (parseError) {
                    return res.status(400).json({ 
                        error: 'Invalid GeoJSON format' 
                    });
                }
            }
            
            // Validate opacity if provided
            if (opacity !== undefined && (opacity < 0 || opacity > 1)) {
                return res.status(400).json({ 
                    error: 'Opacity must be between 0 and 1' 
                });
            }
            
            const updateData = {
                updated_at: new Date().toISOString()
            };
            
            // Only include fields that are provided
            if (name !== undefined) updateData.name = name;
            if (description !== undefined) updateData.description = description;
            if (color !== undefined) updateData.color = color;
            if (opacity !== undefined) updateData.opacity = opacity;
            if (category !== undefined) updateData.category = category;
            if (is_public !== undefined) updateData.is_public = is_public;
            if (is_official !== undefined) updateData.is_official = is_official;
            if (geojson !== undefined) updateData.geojson = geojson;
            if (bounds !== undefined) updateData.bounds = bounds;
            if (assigned_to !== undefined) updateData.assigned_to = assigned_to;
            
            const response = await nocodbService.update(
                config.nocodb.cutsSheetId,
                id,
                updateData
            );
            
            logger.info(`Updated cut: ${existingCut.name} (ID: ${id}) by ${email}`);
            res.json(response);
        } catch (error) {
            logger.error('Error updating cut:', error);
            res.status(500).json({ 
                error: 'Failed to update cut',
                details: error.message 
            });
        }
    }
    
    /**
     * Delete cut - admin only
     */
    async delete(req, res) {
        try {
            const { isAdmin, email } = req.user || {};
            const { id } = req.params;
            
            if (!isAdmin) {
                return res.status(403).json({ error: 'Admin access required' });
            }
            
            // Check if cut exists
            const existingCut = await nocodbService.getById(
                config.nocodb.cutsSheetId,
                id
            );
            
            if (!existingCut) {
                return res.status(404).json({ error: 'Cut not found' });
            }
            
            await nocodbService.delete(
                config.nocodb.cutsSheetId,
                id
            );
            
            logger.info(`Deleted cut: ${existingCut.name} (ID: ${id}) by ${email}`);
            res.json({ message: 'Cut deleted successfully' });
        } catch (error) {
            logger.error('Error deleting cut:', error);
            res.status(500).json({ 
                error: 'Failed to delete cut',
                details: error.message 
            });
        }
    }
    
    /**
     * Get public cuts for map display
     */
    async getPublic(req, res) {
        try {
            // Check if cuts table is configured
            if (!config.nocodb.cutsSheetId) {
                logger.warn('Cuts table not configured - NOCODB_CUTS_SHEET not set');
                return res.json({ list: [] });
            }
            
            logger.info(`Fetching public cuts from table ID: ${config.nocodb.cutsSheetId}`);
            
            // Use getAllPaginated to get ALL cuts, not just first page
            const response = await nocodbService.getAllPaginated(
                config.nocodb.cutsSheetId
            );
            
            logger.info(`Raw response from nocodbService.getAll:`, {
                hasResponse: !!response,
                hasList: !!(response && response.list),
                listLength: response?.list?.length || 0,
                sampleData: response?.list?.[0] || null,
                allFields: response?.list?.[0] ? Object.keys(response.list[0]) : []
            });
            
            // Ensure response has list property
            if (!response || !response.list) {
                logger.warn('No cuts found or invalid response structure');
                return res.json({ list: [] });
            }
            
            // Log all cuts before filtering
            logger.info(`All cuts found: ${response.list.length}`);
            response.list.forEach((cut, index) => {
                // Check multiple possible field names for is_public
                const isPublic = cut.is_public || cut.Is_public || cut['Public Visibility'];
                logger.info(`Cut ${index}: ${cut.name || cut.Name} - is_public: ${isPublic} (type: ${typeof isPublic})`);
                logger.info(`Available fields:`, Object.keys(cut));
            });
            
            // Filter to only public cuts - handle multiple possible field names
            const originalCount = response.list.length;
            response.list = response.list.filter(cut => {
                const isPublic = cut.is_public || cut.Is_public || cut['Public Visibility'];
                return isPublic === true || isPublic === 1 || isPublic === '1';
            });
            const publicCount = response.list.length;
            
            logger.info(`Filtered ${originalCount} total cuts to ${publicCount} public cuts`);
            res.json(response);
        } catch (error) {
            logger.error('Error fetching public cuts:', error);
            // Log more details about the error
            if (error.response) {
                logger.error('Error response:', error.response.data);
                logger.error('Error status:', error.response.status);
            }
            res.status(500).json({ 
                error: 'Failed to fetch public cuts',
                details: error.message 
            });
        }
    }

    /**
     * Get all locations within a cut boundary - admin only
     */
    async getLocationsInCut(req, res) {
        try {
            const { id } = req.params;
            const { isAdmin } = req.user || {};
            
            if (!isAdmin) {
                return res.status(403).json({ error: 'Admin access required' });
            }

            // Get the cut
            const cut = await nocodbService.getById(config.nocodb.cutsSheetId, id);
            if (!cut) {
                return res.status(404).json({ error: 'Cut not found' });
            }

            // Get all locations (use paginated to get ALL records, not just first page)
            const locationsResponse = await nocodbService.getAllPaginated(config.LOCATIONS_TABLE_ID);
            if (!locationsResponse || !locationsResponse.list) {
                return res.json({ locations: [], statistics: { total_locations: 0 } });
            }

            // Apply filters from query params
            const filters = {
                support_level: req.query.support_level,
                has_sign: req.query.has_sign === 'true' ? true : req.query.has_sign === 'false' ? false : undefined,
                sign_size: req.query.sign_size,
                has_email: req.query.has_email === 'true' ? true : req.query.has_email === 'false' ? false : undefined,
                has_phone: req.query.has_phone === 'true' ? true : req.query.has_phone === 'false' ? false : undefined
            };

            // Filter locations within cut boundaries
            const filteredLocations = spatialUtils.filterLocationsInCut(
                locationsResponse.list, 
                cut, 
                filters
            );

            // Calculate statistics
            const statistics = spatialUtils.calculateCutStatistics(filteredLocations);

            const cutName = cut.name || cut.Name || cut.title || cut.Title || 'Unknown';
            logger.info(`Found ${filteredLocations.length} locations in cut: ${cutName}`);
            res.json({ 
                locations: filteredLocations, 
                statistics,
                cut: { id: cut.id || cut.Id || cut.ID, name: cutName }
            });

        } catch (error) {
            logger.error('Error getting locations in cut:', error);
            res.status(500).json({ 
                error: 'Failed to get locations in cut',
                details: error.message 
            });
        }
    }

    /**
     * Export locations within a cut as CSV - admin only
     */
    async exportCutLocations(req, res) {
        try {
            const { id } = req.params;
            const { isAdmin } = req.user || {};
            
            if (!isAdmin) {
                return res.status(403).json({ error: 'Admin access required' });
            }

            // Get the cut
            const cut = await nocodbService.getById(config.nocodb.cutsSheetId, id);
            if (!cut) {
                return res.status(404).json({ error: 'Cut not found' });
            }

            // Check if export is enabled for this cut
            if (cut.export_enabled === false) {
                return res.status(403).json({ error: 'Export is disabled for this cut' });
            }

            // Get all locations (use paginated to get ALL records, not just first page)
            const locationsResponse = await nocodbService.getAllPaginated(config.LOCATIONS_TABLE_ID);
            if (!locationsResponse || !locationsResponse.list) {
                return res.json({ locations: [] });
            }

            // Apply filters from query params
            const filters = {
                support_level: req.query.support_level,
                has_sign: req.query.has_sign === 'true' ? true : req.query.has_sign === 'false' ? false : undefined,
                sign_size: req.query.sign_size,
                has_email: req.query.has_email === 'true' ? true : req.query.has_email === 'false' ? false : undefined,
                has_phone: req.query.has_phone === 'true' ? true : req.query.has_phone === 'false' ? false : undefined
            };

            // Filter locations within cut boundaries
            const filteredLocations = spatialUtils.filterLocationsInCut(
                locationsResponse.list, 
                cut, 
                filters
            );

            // Generate CSV content
            const csvHeaders = [
                'ID', 'First Name', 'Last Name', 'Email', 'Phone', 'Address', 
                'Unit Number', 'Support Level', 'Has Sign', 'Sign Size', 
                'Latitude', 'Longitude', 'Notes'
            ];

            const csvRows = filteredLocations.map(location => [
                location.id || '',
                location.first_name || '',
                location.last_name || '',
                location.email || '',
                location.phone || '',
                location.address || '',
                location.unit_number || '',
                location.support_level || '',
                location.sign ? 'Yes' : 'No',
                location.sign_size || '',
                location.latitude || '',
                location.longitude || '',
                (location.notes || '').replace(/"/g, '""') // Escape quotes in notes
            ]);

            const csvContent = [
                csvHeaders.join(','),
                ...csvRows.map(row => row.map(field => `"${field}"`).join(','))
            ].join('\n');

            const cutName = (cut.name || 'cut').replace(/[^a-zA-Z0-9]/g, '_');
            const timestamp = new Date().toISOString().split('T')[0];
            const filename = `${cutName}_locations_${timestamp}.csv`;

            res.setHeader('Content-Type', 'text/csv');
            res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
            res.send(csvContent);

            logger.info(`Exported ${filteredLocations.length} locations from cut: ${cut.name}`);

        } catch (error) {
            logger.error('Error exporting cut locations:', error);
            res.status(500).json({ 
                error: 'Failed to export cut locations',
                details: error.message 
            });
        }
    }

    /**
     * Update cut settings (visibility, filters, etc.) - admin only
     */
    async updateCutSettings(req, res) {
        try {
            const { id } = req.params;
            const { isAdmin } = req.user || {};
            
            if (!isAdmin) {
                return res.status(403).json({ error: 'Admin access required' });
            }

            const { 
                show_locations, 
                export_enabled, 
                assigned_to, 
                filter_settings,
                last_canvassed,
                completion_percentage 
            } = req.body;

            const updateData = {
                updated_at: new Date().toISOString()
            };

            // Only include fields that are provided
            if (show_locations !== undefined) updateData.show_locations = show_locations;
            if (export_enabled !== undefined) updateData.export_enabled = export_enabled;
            if (assigned_to !== undefined) updateData.assigned_to = assigned_to;
            if (filter_settings !== undefined) updateData.filter_settings = JSON.stringify(filter_settings);
            if (last_canvassed !== undefined) updateData.last_canvassed = last_canvassed;
            if (completion_percentage !== undefined) updateData.completion_percentage = completion_percentage;

            const response = await nocodbService.update(
                config.nocodb.cutsSheetId,
                id,
                updateData
            );

            logger.info(`Updated cut settings for cut ID: ${id}`);
            res.json(response);

        } catch (error) {
            logger.error('Error updating cut settings:', error);
            res.status(500).json({ 
                error: 'Failed to update cut settings',
                details: error.message 
            });
        }
    }

    /**
     * Get cut statistics - admin only
     */
    async getCutStatistics(req, res) {
        try {
            const { id } = req.params;
            const { isAdmin } = req.user || {};
            
            if (!isAdmin) {
                return res.status(403).json({ error: 'Admin access required' });
            }

            // Get the cut
            const cut = await nocodbService.getById(config.nocodb.cutsSheetId, id);
            if (!cut) {
                return res.status(404).json({ error: 'Cut not found' });
            }

            // Get all locations (use paginated to get ALL records, not just first page)
            const locationsResponse = await nocodbService.getAllPaginated(config.LOCATIONS_TABLE_ID);
            if (!locationsResponse || !locationsResponse.list) {
                return res.json({ 
                    statistics: { total_locations: 0 },
                    cut: { id: cut.id, name: cut.name }
                });
            }

            // Get locations within cut boundaries (no additional filters)
            const locationsInCut = spatialUtils.filterLocationsInCut(
                locationsResponse.list, 
                cut, 
                {} // No additional filters for statistics
            );

            // Calculate statistics
            const statistics = spatialUtils.calculateCutStatistics(locationsInCut);

            // Add cut metadata
            const cutStats = {
                ...statistics,
                cut_metadata: {
                    id: cut.id,
                    name: cut.name,
                    category: cut.category,
                    assigned_to: cut.assigned_to,
                    completion_percentage: cut.completion_percentage || 0,
                    last_canvassed: cut.last_canvassed,
                    created_at: cut.created_at
                }
            };

            logger.info(`Generated statistics for cut: ${cut.name}`);
            res.json({ statistics: cutStats });

        } catch (error) {
            logger.error('Error getting cut statistics:', error);
            res.status(500).json({ 
                error: 'Failed to get cut statistics',
                details: error.message 
            });
        }
    }
}

module.exports = new CutsController();
