import express, { Request, Response } from 'express'
import { PrismaClient } from '@prisma/client'
import fs from 'fs'
import path from 'path'
import axios from 'axios'
import multer from 'multer'
import bodyParser from 'body-parser'
import cors from 'cors'
import { Coordinate } from './interfaces/map'
import { generateTilesForZoomLevel } from './utils/tiles'
import { query, validationResult } from 'express-validator'

const prisma = new PrismaClient()
const app = express()
const PORT = process.env.EMS_PORT || 5000

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

app.use(cors())

app.use(bodyParser.json())

app.use(bodyParser.urlencoded({ extended: true }))

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

const upload = multer({ storage: storage })

app.get('/nodes/all', async (req: Request, res: Response) => {
    try {
        const nodes = await prisma.nodes.findMany()

        res.json(nodes)
    } catch (error) {
        console.error('Error getting node:', error);
        res.status(500).json({ error: 'Failed to get node' });
    }
})

app.get('/nodes', query('id').isString().isUUID(), async (req: Request, res: Response) => {
    try {
        const result = validationResult(req)
        if (!result.isEmpty()) {
            return res.send({ errors: result.array() })
        }

        const { id } = req.params

        const node = await prisma.nodes.findFirst({
            where: {
                id: id
            }
        })

        res.json(node)

    } catch (error) {
        console.error('Error getting node:', error);
        res.status(500).json({ error: 'Failed to get node' });
    }
})

app.post('/nodes', async (req: Request, res: Response) => {
    try {
        const { coordinates, object_id, type } = req.body;

        // Convert the incoming array of coordinates into the shape structure
        const shape = coordinates.map((point: number[]) => ({
            object_id: object_id || null,
            x: point[0],
            y: point[1]
        }));

        console.log(shape)

        // Create a new node in the database
        const node = await prisma.nodes.create({
            data: {
                object_id: object_id || null,  // Nullable if object_id is not provided
                shape_type: type,         // You can adjust this dynamically
                shape: shape,                   // Store the shape array as Json[]
                label: 'Default'
            }
        });

        res.status(201).json(node);
    } catch (error) {
        console.error('Error creating node:', error);
        res.status(500).json({ error: 'Failed to create node' });
    }
})

app.post('/upload', upload.single('file'), async (req: Request, res: Response) => {
    const { extentMinX, extentMinY, extentMaxX, extentMaxY, blX, blY, tlX, tlY, trX, trY, brX, brY } = req.body

    const bottomLeft: Coordinate = { x: blX, y: blY }
    const topLeft: Coordinate = { x: tlX, y: tlY }
    const topRight: Coordinate = { x: trX, y: trY }
    const bottomRight: Coordinate = { x: brX, y: brY }

    if (req.file) {
        for (let z = 0; z <= 21; z++) {
            await generateTilesForZoomLevel(uploadDir, tileFolder, req.file, [extentMinX, extentMinY, extentMaxX, extentMaxY], bottomLeft, topLeft, topRight, bottomRight, z)
        }
    }

    return res.status(200)
})

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', 'custom'].includes(provider)) {
        return res.status(400).send('Invalid provider')
    }

    const tilePath = provider === 'custom' ? path.join(tileFolder, provider, z, x, `${y}.png`) : path.join(tileFolder, provider, z, x, `${y}.jpg`)

    if (fs.existsSync(tilePath)) {
        return res.sendFile(tilePath)
    } else {
        if (provider !== 'custom') {
            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')
            }
        } else {
            res.status(404).send('Tile is not generated or not provided')
        }
    }
});

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