import { App } from "@tinyhttp/app";
import bcrypt from "bcryptjs";
import { JWT_SECRET } from "../config/auth.js";
import { prisma } from "../config/database.js";
import { authMiddleware } from "../middleware/auth.js";
import { captchaMiddleware } from "../middleware/captcha.js";
import { captchaConfig } from "../config/captcha.js";
import jwt from "jsonwebtoken";
import fs from "fs/promises";
import passport from "passport";
import { Strategy as GoogleStrategy } from "passport-google-oauth20";

const GOOGLE_CLIENT_ID = process.env["GOOGLE_CLIENT_ID"] || "";
const GOOGLE_CLIENT_SECRET = process.env["GOOGLE_CLIENT_SECRET"] || "";
const GOOGLE_CALLBACK_URL = process.env["GOOGLE_CALLBACK_URL"] || "http://localhost:3000/auth/google/callback";

const SESSION_COOKIE_NAME = "j";
const SESSION_MAX_AGE_SECONDS = 30 * 24 * 60 * 60;
const COOKIE_SAME_SITE = process.env["COOKIE_SAME_SITE"] || "Strict";
const COOKIE_DOMAIN = process.env["COOKIE_DOMAIN"] || "";
const SHOULD_USE_SECURE_COOKIES = (
	process.env["COOKIE_SECURE"] ?? ""
).toLowerCase() !== "false" && (process.env["NODE_ENV"] ?? "production").toLowerCase() !== "development";

function buildSessionCookie(value: string, maxAgeSeconds: number, expiresAt: Date) {
	const encodedValue = encodeURIComponent(value);
	const attributes = [
		`${SESSION_COOKIE_NAME}=${encodedValue}`,
		"HttpOnly",
		"Path=/",
		`Max-Age=${maxAgeSeconds}`,
		`SameSite=${COOKIE_SAME_SITE}`,
		`Expires=${expiresAt.toUTCString()}`
	];

	if (COOKIE_DOMAIN) {
		attributes.push(`Domain=${COOKIE_DOMAIN}`);
	}

	if (SHOULD_USE_SECURE_COOKIES) {
		attributes.push("Secure");
	}

	return attributes.join("; ");
}

function setSessionCookie(res: any, token: string, expiresAt: Date) {
	res.setHeader("Set-Cookie", buildSessionCookie(token, SESSION_MAX_AGE_SECONDS, expiresAt));
}

function clearSessionCookie(res: any) {
	const expiresAt = new Date(0);
	res.setHeader("Set-Cookie", buildSessionCookie("", 0, expiresAt));
}


// Configure Google OAuth Strategy
if (GOOGLE_CLIENT_ID && GOOGLE_CLIENT_SECRET) {
	passport.use(new GoogleStrategy({
		clientID: GOOGLE_CLIENT_ID,
		clientSecret: GOOGLE_CLIENT_SECRET,
		callbackURL: GOOGLE_CALLBACK_URL,
		scope: ["profile", "email"]
	}, async (_accessToken, _refreshToken, profile, done) => {
		try {
			let user = await prisma.user.findFirst({
				where: { googleId: profile.id }
			});

			if (!user) {
				const email = profile.emails?.[0]?.value;
				const name = profile.displayName || profile.emails?.[0]?.value?.split("@")[0] || `user${Date.now()}`;

				user = await prisma.user.create({
					data: {
						googleId: profile.id,
						name,
						email: email ?? null,
						country: "US", // TODO: detect from profile or IP
						droplets: 1000,
						currentCharges: 20,
						maxCharges: 20,
						pixelsPainted: 0,
						level: 1,
						extraColorsBitmap: 0,
						equippedFlag: 0,
						chargesLastUpdatedAt: new Date()
					}
				});
			}

			return done(null, user);
		} catch (error) {
			return done(error as Error, undefined);
		}
	}));
}

export default function (app: App) {
	// Initialize passport
	app.use(passport.initialize() as any);

	// Google OAuth routes
	app.get("/auth/google",
		captchaMiddleware(captchaConfig.login, "token"),
		passport.authenticate("google", { session: false })
	);

	app.get("/auth/google/callback",
		passport.authenticate("google", {
			session: false,
			failureRedirect: "/login"
		}),
		async (req: any, res: any) => {
			try {
				const user = req.user;

				if (!user) {
					return res.redirect("/login?error=auth_failed");
				}

				// Create session
				const session = await prisma.session.create({
					data: {
						userId: user.id,
						expiresAt: new Date(Date.now() + SESSION_MAX_AGE_SECONDS * 1000)
					}
				});

				// Create JWT token
				const token = jwt.sign(
					{
						userId: user.id,
						sessionId: session.id,
						iss: "FurryPlace",
						exp: Math.floor(session.expiresAt.getTime() / 1000),
						iat: Math.floor(Date.now() / 1000)
					},
					JWT_SECRET
				);

				setSessionCookie(res, token, session.expiresAt);

				return res.redirect("/");
			} catch (error) {
				console.error("Google callback error:", error);
				return res.redirect("/login?error=server_error");
			}
		}
	);

	app.get("/login", async (_req, res) => {
		const loginHtml = await fs.readFile("./frontend/login.html", "utf8");
		res.setHeader("Content-Type", "text/html");
		return res.send(loginHtml);
	});

	app.post("/login", captchaMiddleware(captchaConfig.login), async (req: any, res: any) => {
		try {
			const { username, password } = req.body;

			if (!username || !password) {
				return res.status(400)
					.json({ error: "Username and password required" });
			}

			let user = await prisma.user.findFirst({
				where: { name: username }
			});

			if (user) {
				const passwordValid = await bcrypt.compare(password, user.passwordHash ?? "");
				if (!passwordValid) {
					return res.status(401)
						.json({ error: "Invalid username or password" });
				}
			} else {
				// For new user registration, check register captcha if enabled
				if (captchaConfig.register.enabled && captchaConfig.register.provider !== "none") {
					const registerCaptchaToken = req.body?.registerCaptchaToken || req.body?.captchaToken || "";
					if (!registerCaptchaToken) {
						return res.status(400)
							.json({ error: "Registration requires captcha verification" });
					}

					const { verifyCaptcha, getRemoteAddress } = await import("../utils/captcha.js");
					const remoteIp = getRemoteAddress(req);
					const verified = await verifyCaptcha(registerCaptchaToken, captchaConfig.register, remoteIp);

					if (!verified) {
						return res.status(400)
							.json({ error: "Registration captcha verification failed" });
					}
				}

				const passwordHash = await bcrypt.hash(password, 10);

				user = await prisma.user.create({
					data: {
						name: username,
						passwordHash,
						country: "US", // TODO
						droplets: 1000,
						currentCharges: 20,
						maxCharges: 20,
						pixelsPainted: 0,
						level: 1,
						extraColorsBitmap: 0,
						equippedFlag: 0,
						chargesLastUpdatedAt: new Date()
					}
				});
			}

			const session = await prisma.session.create({
				data: {
					userId: user.id,
					expiresAt: new Date(Date.now() + SESSION_MAX_AGE_SECONDS * 1000)
				}
			});

			const token = jwt.sign(
				{
					userId: user.id,
					sessionId: session.id,
					iss: "FurryPlace",
					exp: Math.floor(session.expiresAt.getTime() / 1000),
					iat: Math.floor(Date.now() / 1000)
				},
				JWT_SECRET!
			);

			setSessionCookie(res, token, session.expiresAt);

			return res.json({ success: true });
		} catch (error) {
			console.error("Login error:", error);
			return res.status(500)
				.json({ error: "Internal Server Error" });
		}
	});

	app.post("/auth/logout", authMiddleware, async (req: any, res: any) => {
		try {
			if (req.user?.sessionId) {
				await prisma.session.delete({
					where: { id: req.user.sessionId }
				});
			}

			clearSessionCookie(res);

			return res.json({ success: true });
		} catch (error) {
			console.error("Logout error:", error);
			return res.status(500)
				.json({ error: "Internal Server Error" });
		}
	});
}
