"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.locationsPublicRouter = exports.locationsAdminRouter = void 0;
const express_1 = require("express");
const multer_1 = __importDefault(require("multer"));
const locations_service_1 = require("./locations.service");
const locations_schemas_1 = require("./locations.schemas");
const spatial_1 = require("../../../utils/spatial");
const database_1 = require("../../../config/database");
const validate_1 = require("../../../middleware/validate");
const auth_middleware_1 = require("../../../middleware/auth.middleware");
const rbac_middleware_1 = require("../../../middleware/rbac.middleware");
const roles_1 = require("../../../utils/roles");
// Multer config for CSV upload (memory storage, 10MB limit)
const upload = (0, multer_1.default)({
    storage: multer_1.default.memoryStorage(),
    limits: { fileSize: 10 * 1024 * 1024 },
    fileFilter: (_req, file, cb) => {
        if (file.mimetype === 'text/csv' || file.originalname.endsWith('.csv')) {
            cb(null, true);
        }
        else {
            cb(new Error('Only CSV files are allowed'));
        }
    },
});
// Multer config for bulk import (100MB limit for large NAR files)
const bulkUpload = (0, multer_1.default)({
    storage: multer_1.default.memoryStorage(),
    limits: { fileSize: 100 * 1024 * 1024 },
    fileFilter: (_req, file, cb) => {
        if (file.mimetype === 'text/csv' || file.originalname.endsWith('.csv')) {
            cb(null, true);
        }
        else {
            cb(new Error('Only CSV files are allowed'));
        }
    },
});
// --- Admin Router ---
const adminRouter = (0, express_1.Router)();
exports.locationsAdminRouter = adminRouter;
adminRouter.use(auth_middleware_1.authenticate);
adminRouter.use((0, rbac_middleware_1.requireRole)(...roles_1.MAP_ROLES));
// GET /api/map/locations — list with pagination + filters
adminRouter.get('/', (0, validate_1.validate)(locations_schemas_1.listLocationsSchema, 'query'), async (req, res, next) => {
    try {
        const result = await locations_service_1.locationsService.findAll(req.query);
        res.json(result);
    }
    catch (err) {
        next(err);
    }
});
// GET /api/map/locations/stats — location statistics
adminRouter.get('/stats', async (_req, res, next) => {
    try {
        const stats = await locations_service_1.locationsService.getStats();
        res.json(stats);
    }
    catch (err) {
        next(err);
    }
});
// GET /api/map/locations/export-csv — export as CSV download
adminRouter.get('/export-csv', async (_req, res, next) => {
    try {
        const csv = await locations_service_1.locationsService.exportToCsv();
        res.setHeader('Content-Type', 'text/csv');
        res.setHeader('Content-Disposition', 'attachment; filename=locations.csv');
        res.send(csv);
    }
    catch (err) {
        next(err);
    }
});
// GET /api/map/locations/all — all geocoded locations for admin map
adminRouter.get('/all', async (req, res, next) => {
    try {
        const bounds = req.query.minLat ? {
            minLat: parseFloat(req.query.minLat),
            maxLat: parseFloat(req.query.maxLat),
            minLng: parseFloat(req.query.minLng),
            maxLng: parseFloat(req.query.maxLng),
        } : undefined;
        const limit = req.query.limit ? parseInt(req.query.limit, 10) : 5000;
        const locations = await locations_service_1.locationsService.getAllForMap(bounds, limit);
        // Add header if we hit the limit
        if (locations.length === limit) {
            res.setHeader('X-Location-Limit-Hit', 'true');
        }
        res.json(locations);
    }
    catch (err) {
        next(err);
    }
});
// POST /api/map/locations/reverse-geocode — reverse geocode lat/lng to address
adminRouter.post('/reverse-geocode', (0, validate_1.validate)(locations_schemas_1.reverseGeocodeSchema), async (req, res, next) => {
    try {
        const result = await locations_service_1.locationsService.reverseGeocode(req.body.latitude, req.body.longitude);
        res.json(result);
    }
    catch (err) {
        next(err);
    }
});
// POST /api/map/locations/import-bulk — bulk import (NAR or standard CSV)
adminRouter.post('/import-bulk', bulkUpload.single('file'), async (req, res, next) => {
    // Set 5-minute timeout for large files
    req.setTimeout(5 * 60 * 1000);
    res.setTimeout(5 * 60 * 1000);
    try {
        if (!req.file) {
            res.status(400).json({ error: { message: 'No file uploaded', code: 'NO_FILE' } });
            return;
        }
        const parsed = locations_schemas_1.bulkImportSchema.parse(req.body);
        // Build filters based on filterType
        const filters = {};
        if (parsed.filterType === 'cut' && parsed.cutId) {
            const cut = await database_1.prisma.cut.findUnique({ where: { id: parsed.cutId } });
            if (cut) {
                try {
                    filters.cutPolygon = (0, spatial_1.parseGeoJsonPolygon)(cut.geojson);
                }
                catch { /* ignore invalid polygon */ }
            }
        }
        else if (parsed.filterType === 'mapArea') {
            // Load map settings and compute approximate bounding box
            const settings = await database_1.prisma.mapSettings.findFirst();
            if (settings?.latitude && settings?.longitude && settings?.zoom) {
                const lat = parseFloat(settings.latitude.toString());
                const lng = parseFloat(settings.longitude.toString());
                const zoom = settings.zoom;
                // Approximate visible area for a ~1200x800 viewport
                const degreesPerTile = 360 / Math.pow(2, zoom);
                const halfLng = (1200 / 256 / 2) * degreesPerTile;
                const halfLat = (800 / 256 / 2) * degreesPerTile;
                filters.bounds = {
                    north: lat + halfLat,
                    south: lat - halfLat,
                    east: lng + halfLng,
                    west: lng - halfLng,
                };
            }
        }
        else if (parsed.filterType === 'city' && parsed.filterCity) {
            filters.city = parsed.filterCity;
        }
        else if (parsed.filterType === 'province' && parsed.filterProvince) {
            filters.province = parsed.filterProvince;
        }
        const result = await locations_service_1.locationsService.importBulk(req.file.buffer, req.user.id, parsed, filters);
        res.json(result);
    }
    catch (err) {
        next(err);
    }
});
// GET /api/map/locations/:id — get single location
adminRouter.get('/:id', async (req, res, next) => {
    try {
        const id = req.params.id;
        const location = await locations_service_1.locationsService.findById(id);
        res.json(location);
    }
    catch (err) {
        next(err);
    }
});
// GET /api/map/locations/:id/history — get location edit history
adminRouter.get('/:id/history', async (req, res, next) => {
    try {
        const id = req.params.id;
        const page = req.query.page ? parseInt(req.query.page, 10) : 1;
        const limit = req.query.limit ? parseInt(req.query.limit, 10) : 20;
        const result = await locations_service_1.locationsService.getHistory(id, page, limit);
        res.json(result);
    }
    catch (err) {
        next(err);
    }
});
// POST /api/map/locations — create location
adminRouter.post('/', (0, validate_1.validate)(locations_schemas_1.createLocationSchema), async (req, res, next) => {
    try {
        const location = await locations_service_1.locationsService.create(req.body, req.user.id);
        res.status(201).json(location);
    }
    catch (err) {
        next(err);
    }
});
// PUT /api/map/locations/:id — update location
adminRouter.put('/:id', (0, validate_1.validate)(locations_schemas_1.updateLocationSchema), async (req, res, next) => {
    try {
        const id = req.params.id;
        const location = await locations_service_1.locationsService.update(id, req.body, req.user.id);
        res.json(location);
    }
    catch (err) {
        next(err);
    }
});
// DELETE /api/map/locations/:id — delete location
adminRouter.delete('/:id', async (req, res, next) => {
    try {
        const id = req.params.id;
        await locations_service_1.locationsService.delete(id);
        res.status(204).send();
    }
    catch (err) {
        next(err);
    }
});
// POST /api/map/locations/bulk-delete — bulk delete
adminRouter.post('/bulk-delete', (0, validate_1.validate)(locations_schemas_1.bulkDeleteSchema), async (req, res, next) => {
    try {
        const result = await locations_service_1.locationsService.bulkDelete(req.body.ids);
        res.json(result);
    }
    catch (err) {
        next(err);
    }
});
// POST /api/map/locations/geocode — geocode an address
adminRouter.post('/geocode', (0, validate_1.validate)(locations_schemas_1.geocodeAddressSchema), async (req, res, next) => {
    try {
        const result = await locations_service_1.locationsService.geocodeAddress(req.body.address);
        res.json(result);
    }
    catch (err) {
        next(err);
    }
});
// POST /api/map/locations/geocode-missing — geocode all ungeocoded locations
adminRouter.post('/geocode-missing', async (_req, res, next) => {
    try {
        const result = await locations_service_1.locationsService.geocodeMissing();
        res.json(result);
    }
    catch (err) {
        next(err);
    }
});
// POST /api/map/locations/import-csv — upload + import CSV
adminRouter.post('/import-csv', upload.single('file'), async (req, res, next) => {
    try {
        if (!req.file) {
            res.status(400).json({ error: { message: 'No file uploaded', code: 'NO_FILE' } });
            return;
        }
        const result = await locations_service_1.locationsService.importFromCsv(req.file.buffer, req.user.id);
        res.json(result);
    }
    catch (err) {
        next(err);
    }
});
// --- Public Router ---
const publicRouter = (0, express_1.Router)();
exports.locationsPublicRouter = publicRouter;
// GET /api/map/locations/public — all locations for map (no PII)
publicRouter.get('/public', async (req, res, next) => {
    try {
        const bounds = req.query.minLat ? {
            minLat: parseFloat(req.query.minLat),
            maxLat: parseFloat(req.query.maxLat),
            minLng: parseFloat(req.query.minLng),
            maxLng: parseFloat(req.query.maxLng),
        } : undefined;
        const locations = await locations_service_1.locationsService.getPublicLocations(bounds);
        // Add header if we hit the safety limit
        if (locations.length === 5000) {
            res.setHeader('X-Location-Limit-Hit', 'true');
        }
        res.json(locations);
    }
    catch (err) {
        next(err);
    }
});
//# sourceMappingURL=locations.routes.js.map