// @ts-nocheck
import http from "node:http";
import fs from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { loadConfig } from "./config.ts";
import { testMariaDbConnection } from "./db/mariadb-client.ts";
import { listManufacturers } from "./services/manufacturers.ts";
import { listLanguages } from "./services/languages.ts";
import {
  loadManufacturerCatalogState,
  findProductSources,
  listMappingSources,
  listSuggestedProducts,
  loadProductByEan,
  loadProductInfo,
  getProductPictures,
  saveProductPictureCandidate,
  openProductSource,
} from "./services/catalog-products.ts";
import { openInBrowser } from "./services/browser-adapter.ts";

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const publicDir = path.resolve(__dirname, "../public");
const config = loadConfig();

const mimeTypes = new Map([
  [".html", "text/html; charset=utf-8"],
  [".css", "text/css; charset=utf-8"],
  [".js", "text/javascript; charset=utf-8"],
  [".ts", "text/javascript; charset=utf-8"],
  [".json", "application/json; charset=utf-8"],
  [".svg", "image/svg+xml"],
]);

const server = http.createServer(async (request, response) => {
  try {
    const url = new URL(request.url, `http://${request.headers.host}`);

    if (url.pathname === "/api/status") {
      await sendJson(response, getStatus());
      return;
    }

    if (url.pathname === "/api/database/test" && request.method === "POST") {
      const body = await readJsonBody(request);
      const database = {
        driver: "mariadb",
        host: body.host || "127.0.0.1",
        port: Number(body.port || 3306),
        name: body.name || "",
        user: body.user || "",
        password: body.password || "",
      };
      const result = await testMariaDbConnection(database);
      await sendJson(response, result);
      return;
    }

    const requestConfig = url.pathname.startsWith("/api/")
      ? getRequestConfig(request)
      : null;

    if (url.pathname === "/api/manufacturers") {
      await sendJson(response, await listManufacturers(requestConfig));
      return;
    }

    if (url.pathname === "/api/languages") {
      await sendJson(response, await listLanguages(requestConfig));
      return;
    }

    if (url.pathname === "/api/catalog-maker/product") {
      await sendJson(
        response,
        await loadManufacturerCatalogState(requestConfig, {
          manufacturerId: url.searchParams.get("manufacturerId"),
          position: url.searchParams.get("position") || 1,
        }),
      );
      return;
    }

    if (url.pathname === "/api/catalog-maker/mapping-sources") {
      await sendJson(
        response,
        await listMappingSources(requestConfig, {
          manufacturerId: url.searchParams.get("manufacturerId"),
          checkUrls: url.searchParams.get("check") === "1",
        }),
      );
      return;
    }

    if (url.pathname === "/api/catalog-maker/find-sources") {
      await sendJson(
        response,
        await findProductSources(requestConfig, {
          manufacturerId: url.searchParams.get("manufacturerId"),
          sourceKey: url.searchParams.get("sourceKey"),
          productName: url.searchParams.get("productName"),
          supplierReference: url.searchParams.get("supplierReference"),
          ean: url.searchParams.get("ean"),
          eanGroup: url.searchParams.get("eanGroup"),
          color: url.searchParams.get("color"),
        }),
      );
      return;
    }

    if (url.pathname === "/api/catalog-maker/get-pictures") {
      await sendJson(
        response,
        await getProductPictures(requestConfig, {
          manufacturerId: url.searchParams.get("manufacturerId"),
          sourceKey: url.searchParams.get("sourceKey"),
          productName: url.searchParams.get("productName"),
          supplierReference: url.searchParams.get("supplierReference"),
          ean: url.searchParams.get("ean"),
          eanGroup: url.searchParams.get("eanGroup"),
          color: url.searchParams.get("color"),
        }),
      );
      return;
    }

    if (url.pathname === "/api/catalog-maker/save-picture" && request.method === "POST") {
      const body = await readJsonBody(request);
      await sendJson(response, await saveProductPictureCandidate(requestConfig, body));
      return;
    }

    if (url.pathname === "/api/catalog-maker/open-source") {
      await sendJson(
        response,
        await openProductSource(requestConfig, {
          manufacturerId: url.searchParams.get("manufacturerId"),
          sourceKey: url.searchParams.get("sourceKey"),
          url: url.searchParams.get("url"),
        }),
      );
      return;
    }

    if (url.pathname === "/api/tools/open-adminer") {
      await openInBrowser(config.databaseTool.url, { engine: config.databaseTool.browserEngine });
      await sendJson(response, {
        opened: true,
        url: config.databaseTool.url,
        browserEngine: config.databaseTool.browserEngine,
      });
      return;
    }

    if (url.pathname === "/api/tools/open-dbgate") {
      await openInBrowser(config.dbGate.url, { engine: config.dbGate.browserEngine });
      await sendJson(response, {
        opened: true,
        url: config.dbGate.url,
        browserEngine: config.dbGate.browserEngine,
      });
      return;
    }

    if (url.pathname === "/api/catalog-maker/product-info") {
      await sendJson(
        response,
        await loadProductInfo(requestConfig, {
          productId: url.searchParams.get("productId"),
          manufacturerId: url.searchParams.get("manufacturerId"),
          supplierReference: url.searchParams.get("supplierReference"),
        }),
      );
      return;
    }

    if (url.pathname === "/api/catalog-maker/product-by-ean") {
      await sendJson(
        response,
        await loadProductByEan(requestConfig, {
          manufacturerId: url.searchParams.get("manufacturerId"),
          ean: url.searchParams.get("ean"),
        }),
      );
      return;
    }

    if (url.pathname === "/api/catalog-maker/suggested-products") {
      await sendJson(
        response,
        await listSuggestedProducts(requestConfig, {
          manufacturerId: url.searchParams.get("manufacturerId"),
          supplierReference: url.searchParams.get("supplierReference"),
          productName: url.searchParams.get("productName"),
        }),
      );
      return;
    }

    const requestedPath = url.pathname === "/" ? "/index.html" : url.pathname;
    const safePath = path.normalize(requestedPath).replace(/^(\.\.[/\\])+/, "");
    const filePath = path.join(publicDir, safePath);

    if (!filePath.startsWith(publicDir)) {
      response.writeHead(403);
      response.end("Forbidden");
      return;
    }

    const content = await fs.readFile(filePath);
    response.writeHead(200, {
      "content-type": mimeTypes.get(path.extname(filePath)) || "application/octet-stream",
    });
    response.end(content);
  } catch (error) {
    if (error.code === "ENOENT") {
      response.writeHead(404);
      response.end("Not found");
      return;
    }

    await sendJson(response, {
      message: error.message || "Internal server error",
    }, 500);
  }
});

server.listen(config.server.port, config.server.host, () => {
  console.log(`Catalog Maker preview: http://${config.server.host}:${config.server.port}`);
});

function getStatus() {
  return {
    mode: config.dryRun ? "dry-run" : "live",
    databaseAccess: "read-only",
    database: {
      driver: config.database.driver,
      mode: config.database.mode,
      scrapeName: config.database.scrapeName,
      allowLocalWrites: config.database.driver === "mariadb"
        && config.database.mode === "local"
        && config.database.allowLocalWrites,
    },
    workbook: path.basename(config.sourceWorkbook),
    workbookExists: true,
    steps: [
      ["load-context", "Ready", "Workbook and local config"],
      ["load-manufacturer", "Mapping", "Manufacturer and product selection"],
      ["check-stock", "Mapping", "Supplier stock and existing catalog state"],
      ["build-combinations", "Mapping", "Product combinations"],
      ["update-prices", "Mapping", "Price calculation preview"],
      ["update-texts", "Mapping", "Product card text generation"],
      ["handle-pictures", "Mapping", "Cover and detail pictures"],
    ],
  };
}

function getRequestConfig(request) {
  const requestedMode = String(request.headers["x-database-mode"] || config.database.mode).toLowerCase();
  const selectedMode = requestedMode === "scrape" ? "local" : requestedMode;

  if (selectedMode === "local" && config.database.driver !== "mariadb") {
    throw new Error("Local DB is selected, but MariaDB is not configured. Live DB was not used.");
  }

  if (selectedMode === "live" && config.database.driver === "mariadb") {
    throw new Error("Live DB is selected, but this project is configured for local MariaDB.");
  }

  return {
    ...config,
    database: {
      ...config.database,
      mode: selectedMode,
      allowLocalWrites: selectedMode === "local"
        && config.database.allowLocalWrites
        && request.headers["x-local-write-access"] === "1",
    },
  };
}

async function sendJson(response, data, statusCode = 200) {
  response.writeHead(statusCode, { "content-type": "application/json; charset=utf-8" });
  response.end(JSON.stringify(data));
}

async function readJsonBody(request) {
  let body = "";
  for await (const chunk of request) body += chunk;
  return body ? JSON.parse(body) : {};
}
