import express, { Request, Response } from 'express';
import fs from 'fs';
import path from 'path';
import axios from 'axios';
import multer from 'multer'
import sharp from 'sharp';
import { pipeline } from 'stream';
import pump from 'pump'
import md5 from 'md5'
import bodyParser from 'body-parser';
import cors from 'cors'

const app = express();
const PORT = process.env.EMS_PORT || 5000;

const tileFolder = path.join(__dirname, '..', 'public', 'tile_data');
const uploadDir = path.join(__dirname, '..', 'public', 'uploads');

interface UploadProgress {
    receivedChunks: Set<number>;
    totalChunks: number;
}

const uploadProgress: Record<string, UploadProgress> = {};

app.use(bodyParser.raw({
    type: 'application/octet-stream',
    limit: '100mb'
}))

app.use(cors())

// Upload chunk handler
app.post('/upload', (req: Request, res: Response) => {
    const chunkNumber = parseInt(req.headers['x-chunk-number'] as string, 10);
    const totalChunks = parseInt(req.headers['x-total-chunks'] as string, 10);
    const fileId = req.headers['x-file-id'] as string;

    if (isNaN(chunkNumber) || isNaN(totalChunks) || !fileId) {
        return res.status(400).send('Invalid headers');
    }

    const chunkDir = path.join(uploadDir, fileId);
    if (!fs.existsSync(chunkDir)) {
        fs.mkdirSync(chunkDir, { recursive: true });
    }

    // Save the chunk
    const chunkPath = path.join(chunkDir, `chunk-${chunkNumber}`);
    fs.writeFileSync(chunkPath, req.body);

    // Initialize or update upload progress
    if (!uploadProgress[fileId]) {
        uploadProgress[fileId] = { receivedChunks: new Set(), totalChunks };
    }
    uploadProgress[fileId].receivedChunks.add(chunkNumber);

    // Check if all chunks have been received
    if (uploadProgress[fileId].receivedChunks.size === totalChunks) {
        assembleChunks(fileId, chunkDir)
            .then(() => {
                delete uploadProgress[fileId]; // Clean up progress tracking
                res.status(200).send('File assembled successfully');
            })
            .catch((error) => {
                console.error('Error assembling file:', error);
                res.status(500).send('Failed to assemble file');
            });
    } else {
        res.status(200).send('Chunk received');
    }
});

// Assemble chunks into final file
async function assembleChunks(fileId: string, chunkDir: string): Promise<void> {
    const finalPath = path.join(uploadDir, fileId);
    const chunks = fs.readdirSync(chunkDir).sort((a, b) => {
        const numA = parseInt(a.split('-')[1]);
        const numB = parseInt(b.split('-')[1]);
        return numA - numB;
    });

    const writeStream = fs.createWriteStream(finalPath);
    for (const chunk of chunks) {
        const chunkPath = path.join(chunkDir, chunk);
        const data = fs.readFileSync(chunkPath);
        writeStream.write(data);
        fs.unlinkSync(chunkPath); // Remove chunk after writing
    }

    writeStream.end(() => {
        fs.rmdirSync(chunkDir); // Remove temporary chunk directory
    });
}

const storage = multer.diskStorage({
    destination: function (req, file, cb) {
        cb(null, path.join(__dirname, '..', 'public', 'uploads'))
    },
    filename: function (req, file, cb) {
        cb(null, file.originalname)
    }
})

const upload = multer({ storage: storage })

const fetchTileFromAPI = async (provider: string, z: string, x: string, y: string): Promise<Buffer> => {
    const url = provider === 'google'
        ? `https://khms2.google.com/kh/v=984?x=${x}&y=${y}&z=${z}`
        : `https://core-sat.maps.yandex.net/tiles?l=sat&x=${x}&y=${y}&z=${z}&scale=1&lang=ru_RU`;

    const response = await axios.get(url, { responseType: 'arraybuffer' });
    return response.data;
}

app.get('/tile/:provider/:z/:x/:y', async (req: Request, res: Response) => {
    const { provider, z, x, y } = req.params;

    if (!['google', 'yandex'].includes(provider)) {
        return res.status(400).send('Invalid provider');
    }

    const tilePath = path.join(tileFolder, provider, z, x, `${y}.jpg`);

    if (fs.existsSync(tilePath)) {
        return res.sendFile(tilePath);
    }

    try {
        const tileData = await fetchTileFromAPI(provider, z, x, y);

        fs.mkdirSync(path.dirname(tilePath), { recursive: true });

        fs.writeFileSync(tilePath, tileData);

        res.contentType('image/jpeg');
        res.send(tileData);
    } catch (error) {
        console.error('Error fetching tile from API:', error);
        res.status(500).send('Error fetching tile from API');
    }
});

app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
