import express from "express";
import bodyParser from "body-parser";
import cors from 'cors';
import { getData, getDateForWeekIndex } from "./service";
import dotenv from 'dotenv';
import path from 'path';
import { getQr } from "./qr";
import { generateToken, verify } from "./auth";
import { InsufficientPermissions } from "./utils";
import { initWebsocket } from "./websocket";
import pizzaDayRoutes from "./routes/pizzaDayRoutes";
import foodRoutes from "./routes/foodRoutes";
import votingRoutes from "./routes/votingRoutes";

const ENVIRONMENT = process.env.NODE_ENV || 'production';
dotenv.config({ path: path.resolve(__dirname, `./.env.${ENVIRONMENT}`) });

// Validace nastavení JWT tokenu - nemá bez něj smysl vůbec povolit server spustit
if (!process.env.JWT_SECRET) {
    throw Error("Není vyplněna proměnná prostředí JWT_SECRET");
}

const app = express();
const server = require("http").createServer(app);
initWebsocket(server);

// Body-parser middleware for parsing JSON
app.use(bodyParser.json());

app.use(cors({
    origin: '*'
}));

// Zapínatelný login přes hlavičky - pokud je zapnutý nepovolí "basicauth"
const HTTP_REMOTE_USER_ENABLED = process.env.HTTP_REMOTE_USER_ENABLED || false;
const HTTP_REMOTE_USER_HEADER_NAME = process.env.HTTP_REMOTE_USER_HEADER_NAME || 'remote-user';
let HTTP_REMOTE_TRUSTED_IPS = ['127.0.0.1']
if (HTTP_REMOTE_USER_ENABLED) {
    if (!process.env.HTTP_REMOTE_TRUSTED_IPS) {
        throw new Error('Zapnutý login z hlaviček, ale není nastaven rozsah adres z kterých hlavička může přijít.');
    }
    HTTP_REMOTE_TRUSTED_IPS = process.env.HTTP_REMOTE_TRUSTED_IPS.split(',');
    //TODO: nevim jak udelat console.log pouze pro "debug"
    //console.log("Budu věřit hlavičkám z: " + HTTP_REMOTE_TRUSTED_IPS);
    app.set('trust proxy', HTTP_REMOTE_TRUSTED_IPS);
    app.enable('trust proxy');
    console.log('Zapnutý login přes hlaviček z proxy.');
}


// ----------- Metody nevyžadující token --------------

app.get("/api/whoami", (req, res) => {
    if (!HTTP_REMOTE_USER_ENABLED) {
        res.status(403).json({ error: 'Není zapnuté přihlášení z hlaviček' });
    }
    res.send(req.header(HTTP_REMOTE_USER_HEADER_NAME));
})

app.post("/api/login", (req, res) => {
    if (HTTP_REMOTE_USER_ENABLED) { // je rovno app.enabled('trust proxy')
        // Autentizace pomocí trusted headers
        const remoteUser = req.header(HTTP_REMOTE_USER_HEADER_NAME);
        const remoteName = req.header('remote-name');
        if (remoteUser && remoteUser.length > 0 && remoteName && remoteName.length > 0) {
            res.status(200).json(generateToken(Buffer.from(remoteName, 'latin1').toString(), true));
        }
    } else {
        // Klasická autentizace loginem
        if (!req.body?.login || req.body.login.trim().length === 0) {
            throw Error("Nebyl předán login");
        }
        // TODO zavést podmínky pro délku loginu (min i max)
        res.status(200).json(generateToken(req.body.login, false));
    }
});

// TODO dočasné řešení - QR se zobrazuje přes <img>, nemáme sem jak dostat token
app.get("/api/qr", (req, res) => {
    // const login = getLogin(parseToken(req));
    if (!req.query?.login) {
        throw Error("Nebyl předán login");
    }
    const img = getQr(req.query.login as string);
    res.writeHead(200, {
        'Content-Type': 'image/png',
        'Content-Length': img.length
    });
    res.end(img);
});

// ----------------------------------------------------

/** Middleware ověřující JWT token */
app.use("/api/", (req, res, next) => {
    if (HTTP_REMOTE_USER_ENABLED) {
        const userHeader = req.header(HTTP_REMOTE_USER_HEADER_NAME);
        const nameHeader = req.header('remote-name');
        const emailHeader = req.header('remote-email');
        if (userHeader !== undefined && nameHeader !== undefined) {
            const remoteName = Buffer.from(nameHeader, 'latin1').toString();
            console.log("Tvuj username, name a email: %s, %s, %s.", userHeader, remoteName, emailHeader);
        }
    }
    if (!req.headers.authorization) {
        return res.status(401).json({ error: 'Nebyl předán autentizační token' });
    }
    const token = req.headers.authorization.split(' ')[1];
    if (!verify(token)) {
        return res.status(403).json({ error: 'Neplatný autentizační token' });
    }
    next();
});

/** Vrátí data pro aktuální den. */
app.get("/api/data", async (req, res) => {
    let date = undefined;
    if (req.query.dayIndex != null && typeof req.query.dayIndex === 'string') {
        const index = parseInt(req.query.dayIndex);
        if (!isNaN(index)) {
            date = getDateForWeekIndex(parseInt(req.query.dayIndex));
        }
    }
    res.status(200).json(await getData(date));
});

// Ostatní routes
app.use("/api/pizzaDay", pizzaDayRoutes);
app.use("/api/food", foodRoutes);
app.use("/api/voting", votingRoutes);
app.use(express.static('public'))

// Middleware pro zpracování chyb
app.use((err: any, req: any, res: any, next: any) => {
    if (err instanceof InsufficientPermissions) {
        res.status(403).send({ error: err.message })
    } else {
        res.status(500).send({ error: err.message })
    }
    next();
});

const PORT = process.env.PORT || 3001;
const HOST = process.env.HOST || '0.0.0.0';

server.listen(PORT, () => {
    console.log(`Server listening on ${HOST}, port ${PORT}`);
});

// Umožníme vypnutí serveru přes SIGINT, jinak Docker čeká než ho sestřelí
process.on('SIGINT', function () {
    console.log("\nSIGINT (Ctrl-C), vypínám server");
    process.exit(0);
});