import { Router } from "express";

import { asyncHandler } from "../../lib/async-handler.js";
import { HttpError } from "../../lib/http-error.js";
import { requireAuth, requireRole } from "../../middleware/auth.js";
import { validate } from "../../middleware/validate.js";
import { sendTrackingTestEmail } from "./mail.service.js";
import {
  getCompanySettings,
  getInspectionEmailSettings,
  saveCompanySettings,
  saveInspectionEmailSettings,
} from "./settings.service.js";
import {
  companyRouteQuerySchema,
  updateCompanySettingsSchema,
  updateInspectionEmailSettingsSchema,
} from "./settings.schemas.js";

type GeocodeSearchResult = {
  lat: string;
  lon: string;
  display_name?: string;
};

type OsrmRouteResponse = {
  code: string;
  routes?: Array<{
    distance: number;
    duration: number;
  }>;
};

type RouteSummary = {
  distanceKm: number;
  durationMinutes: number;
  companyAddress: string;
};

const routeCache = new Map<string, { item: RouteSummary; fetchedAt: number }>();
const ROUTE_CACHE_TTL_MS = 1000 * 60 * 5;

const buildRouteCacheKey = (lat: number, lon: number, companyLat: number, companyLon: number) =>
  `${lat.toFixed(4)}:${lon.toFixed(4)}:${companyLat.toFixed(4)}:${companyLon.toFixed(4)}`;

export const settingsRouter = Router();

settingsRouter.use(requireAuth);

settingsRouter.get(
  "/company",
  asyncHandler(async (_request, response) => {
    const item = await getCompanySettings();
    response.json({ item });
  }),
);

settingsRouter.patch(
  "/company",
  requireRole("admin"),
  validate(updateCompanySettingsSchema),
  asyncHandler(async (request, response) => {
    const url = new URL("https://nominatim.openstreetmap.org/search");
    url.searchParams.set("format", "jsonv2");
    url.searchParams.set("limit", "1");
    url.searchParams.set("q", request.body.address);

    const geocodeResponse = await fetch(url, {
      headers: {
        "User-Agent": "VisauTracker/1.0 (track.visau.duckdns.org)",
        Accept: "application/json",
      },
    });

    if (!geocodeResponse.ok) {
      throw new HttpError(502, "Nie udało się znaleźć adresu firmy");
    }

    const payload = (await geocodeResponse.json()) as GeocodeSearchResult[];
    const firstMatch = payload[0];
    if (!firstMatch) {
      throw new HttpError(404, "Nie znaleziono adresu firmy");
    }

    const lat = Number(firstMatch.lat);
    const lon = Number(firstMatch.lon);
    if (!Number.isFinite(lat) || !Number.isFinite(lon)) {
      throw new HttpError(502, "Adres firmy ma nieprawidłowe współrzędne");
    }

    const saved = await saveCompanySettings({
      addressInput: request.body.address,
      addressLabel: firstMatch.display_name?.trim() || request.body.address,
      lat,
      lon,
    });

    response.json({ item: saved });
  }),
);

settingsRouter.get(
  "/company-route",
  validate(companyRouteQuerySchema),
  asyncHandler(async (request, response) => {
    const lat = Number(request.query.lat);
    const lon = Number(request.query.lon);
    const company = await getCompanySettings();
    if (!company) {
      throw new HttpError(404, "Brak ustawionego adresu firmy");
    }

    const cacheKey = buildRouteCacheKey(lat, lon, company.lat, company.lon);
    const cached = routeCache.get(cacheKey);
    if (cached && Date.now() - cached.fetchedAt < ROUTE_CACHE_TTL_MS) {
      return response.json({ item: cached.item });
    }

    const osrmUrl = new URL(
      `https://router.project-osrm.org/route/v1/driving/${lon},${lat};${company.lon},${company.lat}`,
    );
    osrmUrl.searchParams.set("alternatives", "false");
    osrmUrl.searchParams.set("overview", "false");
    osrmUrl.searchParams.set("steps", "false");

    const osrmResponse = await fetch(osrmUrl, {
      headers: {
        "User-Agent": "VisauTracker/1.0 (track.visau.duckdns.org)",
        Accept: "application/json",
      },
    });

    if (!osrmResponse.ok) {
      throw new HttpError(502, "Nie udało się policzyć trasy do firmy");
    }

    const payload = (await osrmResponse.json()) as OsrmRouteResponse;
    const route = payload.routes?.[0];
    if (!route) {
      throw new HttpError(404, "Brak trasy do firmy");
    }

    const item: RouteSummary = {
      distanceKm: route.distance / 1000,
      durationMinutes: route.duration / 60,
      companyAddress: company.addressLabel,
    };

    routeCache.set(cacheKey, { item, fetchedAt: Date.now() });

    response.json({ item });
  }),
);

settingsRouter.post(
  "/email/test",
  requireRole("admin"),
  asyncHandler(async (_request, response) => {
    const item = await sendTrackingTestEmail();
    response.json({ item });
  }),
);

settingsRouter.get(
  "/inspection-email",
  requireRole("admin"),
  asyncHandler(async (_request, response) => {
    const item = await getInspectionEmailSettings();
    response.json({ item });
  }),
);

settingsRouter.patch(
  "/inspection-email",
  requireRole("admin"),
  validate(updateInspectionEmailSettingsSchema),
  asyncHandler(async (request, response) => {
    const item = await saveInspectionEmailSettings({
      recipients: request.body.recipients,
    });
    response.json({ item });
  }),
);
