import "reflect-metadata";
import dotenv from "dotenv";
import express, { Request, Response, NextFunction } from "express";
import "express-async-errors"; // needed for global error catching, reducing the try/catch all over the place
import cors from "cors";
import helmet from "helmet";
import os from "os";
import swaggerUi from "swagger-ui-express";
import swaggerJson from "./swagger.json";
import { RegisterRoutes } from "~/generated/routes";

import cacheRouter from "~/routes/cache";
import routeUsers from "~/routes/users";
import dashboardRouter from "~/routes/dashboard";
import lockRouter from "~/routes/lock";
import itemsRouter from "~/routes/item/items";
import itemSetsRouter from "~/routes/item/sets";
import itemBundleRouter from "~/routes/item/bundle";
import itemBundlesRouter from "~/routes/item/bundles";
import suitsRouter from "~/routes/suit/index";
// import suitsItemsRouter from "~/routes/suit/items";
import itemGroupsRouter from "~/routes/item/groups";
import itemSubGroupsRouter from "~/routes/item/subGroups";
import itemCategoriesRouter from "~/routes/item/categories";
import itemSubCategoriesRouter from "~/routes/item/subCategories";
import sourcesRouter from "~/routes/sources";
import statusRouter from "~/routes/status";
import makersRouter from "~/routes/makers";
import makersByCategoryRouter from "~/routes/makersByCategory";
import photosRouter from "~/routes/photos";
import financialsRouter from "~/routes/financials";

import libraryRouter from "~/routes/library";

import { getBasePhotoPath } from "~/functions/isLinuxServer";

// japanese weapons
// import japaneseWeaponSwordRouter from "~/routes/japanese/weapon/sword";

// european weapons
// import europeanWeaponSwordRouter from "~/routes/medieval/weapon/sword";

// japanese armor
import japaneseArmorKabutoRouter from "~/routes/japanese/armor/kabuto";
import japaneseArmorMempoRouter from "~/routes/japanese/armor/mempo";
import japaneseArmorDoRouter from "~/routes/japanese/armor/do";
import japaneseArmorSodeRouter from "~/routes/japanese/armor/sode";
import japaneseArmorKoteRouter from "~/routes/japanese/armor/kote";
import japaneseArmorHaidateRouter from "~/routes/japanese/armor/haidate";
import japaneseArmorSuneateRouter from "~/routes/japanese/armor/suneate";

// core
import coreRouter from "~/routes/core";

import weaponsRouter from "~/routes/weapons";
import armorRouter from "~/routes/armor";
import clothingRouter from "~/routes/clothing";
import decorRouter from "~/routes/decor";

// medieval weapons

// medieval armor

// reports
import reportsRouter from "~/routes/reports";

// wishlist
import wishlistRouter from "~/routes/wishlist";
import "~/functions/cache";

dotenv.config({ path: require("find-config")(".env") });

if (!process.env.PORT) {
	process.exit(1);
}

const PORT: number = parseInt(process.env.PORT as string, 10);

const app = express();

const hostname = os.hostname();

app.use(helmet());

app.use(cors());
app.use(helmet.crossOriginResourcePolicy({ policy: "cross-origin" }));

app.use(express.json());

// make token available globally on all routes
app.use("*", function (_req: Request, _res: Response, next: NextFunction) {
	const method = _req.method.toUpperCase();
	const path = _req.baseUrl.toUpperCase().replace("/API", "");
	const noTokenNeeded =
		(method === "POST" && path === "/USERS") ||
		(method === "GET" && path.indexOf("/LIBRARY") === 0) ||
		(method === "GET" && path === "/WISHLIST/PUBLIC") ||
		path.indexOf(".JPG") > 0 ||
		path.indexOf(".JPEG") > 0 ||
		path.indexOf(".PNG") > 0 ||
		path.indexOf(".GIF") > 0;

	if (hostname === "office") {
		console.log(`${_req?.method} ${_req.baseUrl}${_req.url}`);
		// console.log(`${_req?.method} ${_req.baseUrl}`);

		if (_req?.params?.length > 0) {
			console.log(`\n  >> PARAMS: `, _req.params);
		}

		if (_req?.query?.length > 0) {
			console.log(`\n  >> QUERY:`, _req.query, `\n`);
		}
	}

	next();
});

app.use(
	express.urlencoded({
		extended: true,
	}),
);

// landing page / heartbeat check
app.get("/", (_req: Request, res: Response) => {
	res.json({ message: "ok" });
});

// custom routes
app.use("/clearCache", cacheRouter);

// app.use("/auth", authRouter);
app.use("/users", routeUsers);
app.use("/dashboard", dashboardRouter);
app.use("/lock", lockRouter);

app.use("/items", itemsRouter);
app.use("/itemSets", itemSetsRouter);
app.use("/itemBundles", itemBundlesRouter);
app.use("/suits", suitsRouter);
// app.use("/suits/:fullsuitID/items", suitsItemsRouter);
app.use("/itemBundle", itemBundleRouter);
app.use("/itemGroups", itemGroupsRouter);
app.use("/itemSubGroups", itemSubGroupsRouter);
app.use("/itemCategories", itemCategoriesRouter);
app.use("/itemSubCategories", itemSubCategoriesRouter);
app.use("/sources", sourcesRouter);
app.use("/status", statusRouter);
app.use("/makers", makersRouter);
app.use("/makersByCategory", makersByCategoryRouter);
app.use("/photos", photosRouter);
app.use("/financials", financialsRouter);

app.use("/library", libraryRouter);

// one controller to rule them all!
// or at least most of the ones for item stuff
app.use("/core", coreRouter);

// japanese weapons
// app.use("/japanese/weapon/sword", japaneseWeaponSwordRouter);

// japanese armor
app.use("/items", japaneseArmorKabutoRouter);
app.use("/items", japaneseArmorMempoRouter);
app.use("/items", japaneseArmorDoRouter);
app.use("/items", japaneseArmorSodeRouter);
app.use("/items", japaneseArmorKoteRouter);
app.use("/items", japaneseArmorHaidateRouter);
app.use("/items", japaneseArmorSuneateRouter);

app.use("/weapons", weaponsRouter);
app.use("/armor", armorRouter);
app.use("/clothing", clothingRouter);
app.use("/decor", decorRouter);

// medieval weapons
// app.use("/medieval/weapon/sword", europeanWeaponSwordRouter);

// medieval armor

// reports
app.use("/reports", reportsRouter);

// wishlist
app.use("/wishlist", wishlistRouter);

app.use("/photos", express.static(getBasePhotoPath()));

const errorHandling = (err, req: Request, res: Response, next: NextFunction) => {
	console.log(err);

	res.status(500).json({
		msg: err,
		success: false,
	});
};

// app.use(errorHandling);

// console logging for development
// THIS SHOULD CATCH ALL ROUTES IT DOES NOT FIND A MATCH FOR
app.use((req: Request, _res: Response, next: NextFunction) => {
	if (req.url.indexOf(".jpg") === -1) {
		console.log("\n>> MISSING ROUTE:\n\n   url:", req.url, "\n   method:", req.method, "\n   body:", req.body, "\n");
	}
	next();

	return;
});

// Register TSOA routes
RegisterRoutes(app);

// Setup Swagger UI with generated spec
// Sort paths alphabetically by URL
const sortedSpec = { ...swaggerJson };
if (sortedSpec.paths) {
	const sortedPaths: any = {};
	Object.keys(sortedSpec.paths)
		.sort((a, b) => a.localeCompare(b))
		.forEach((key) => {
			sortedPaths[key] = sortedSpec.paths[key];
		});
	sortedSpec.paths = sortedPaths;
}

const swaggerUiOptions = {
	tagsSorter: "alpha",
};
app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(sortedSpec as any, swaggerUiOptions));

// listen for requests
app.listen(PORT, () => {
	console.log(`API listening at http://localhost:${PORT}\n\n`);
	console.log(`Swagger docs available on http://localhost:${PORT}/api-docs`);
});
