import type { AuthenticatedRequest } from "../../../types/index.js";
import express, { type Request, type Response } from "express";
import { dashboardLogger } from "../../utils/logger.js";
import { DatabaseSaveTrigger } from "../../utils/database-save-trigger.js";
import { isNonEmptyString } from "./host-normalizers.js";
import {
  createCurrentDashboardServiceLinkRepository,
  createCurrentSyncTombstoneRepository,
} from "../repositories/factory.js";

export const dashboardServiceLinksRouter = express.Router();

function isValidUrl(url: string): boolean {
  try {
    const parsed = new URL(url);
    return parsed.protocol === "http:" || parsed.protocol === "https:";
  } catch {
    return false;
  }
}

/**
 * @openapi
 * /service-links:
 *   get:
 *     summary: Get service links
 *     description: Returns all dashboard service links for the authenticated user.
 *     tags:
 *       - Dashboard
 *     responses:
 *       200:
 *         description: List of service links.
 *       500:
 *         description: Failed to fetch service links.
 */
dashboardServiceLinksRouter.get("/", async (req: Request, res: Response) => {
  const userId = (req as AuthenticatedRequest).userId;
  try {
    const links =
      await createCurrentDashboardServiceLinkRepository().listByUserId(userId);
    res.json(links);
  } catch (err) {
    dashboardLogger.error("Failed to fetch service links", err);
    res.status(500).json({ error: "Failed to fetch service links" });
  }
});

/**
 * @openapi
 * /service-links:
 *   post:
 *     summary: Create service link
 *     description: Creates a new dashboard service link for the authenticated user.
 *     tags:
 *       - Dashboard
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             required:
 *               - label
 *               - url
 *             properties:
 *               label:
 *                 type: string
 *               url:
 *                 type: string
 *     responses:
 *       201:
 *         description: Service link created.
 *       400:
 *         description: Invalid data.
 *       500:
 *         description: Failed to create service link.
 */
dashboardServiceLinksRouter.post("/", async (req: Request, res: Response) => {
  const userId = (req as AuthenticatedRequest).userId;
  const { label, url } = req.body;

  if (!isNonEmptyString(label) || !isNonEmptyString(url)) {
    return res.status(400).json({ error: "label and url are required" });
  }
  if (!isValidUrl(url)) {
    return res
      .status(400)
      .json({ error: "url must be a valid http or https URL" });
  }

  try {
    const created =
      await createCurrentDashboardServiceLinkRepository().createForUser(
        userId,
        {
          label: label.trim(),
          url: url.trim(),
        },
      );

    DatabaseSaveTrigger.triggerSave("dashboard_service_link_created");
    res.status(201).json(created);
  } catch (err) {
    dashboardLogger.error("Failed to create service link", err);
    res.status(500).json({ error: "Failed to create service link" });
  }
});

/**
 * @openapi
 * /service-links/{id}:
 *   delete:
 *     summary: Delete service link
 *     description: Deletes a dashboard service link by ID.
 *     tags:
 *       - Dashboard
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: integer
 *     responses:
 *       200:
 *         description: Service link deleted.
 *       400:
 *         description: Invalid id.
 *       404:
 *         description: Not found.
 *       500:
 *         description: Failed to delete service link.
 */
dashboardServiceLinksRouter.delete(
  "/:id",
  async (req: Request, res: Response) => {
    const userId = (req as AuthenticatedRequest).userId;
    const idParam = Array.isArray(req.params.id)
      ? req.params.id[0]
      : req.params.id;
    const id = parseInt(idParam);

    if (isNaN(id)) {
      return res.status(400).json({ error: "Invalid id" });
    }

    try {
      const existing =
        await createCurrentDashboardServiceLinkRepository().findByIdForUser(
          userId,
          id,
        );

      if (!existing) {
        return res.status(404).json({ error: "Not found" });
      }

      const deleted =
        await createCurrentDashboardServiceLinkRepository().deleteForUser(
          userId,
          id,
        );
      if (deleted?.syncId) {
        await createCurrentSyncTombstoneRepository().record(
          userId,
          "dashboardServiceLinks",
          deleted.syncId,
        );
      }

      DatabaseSaveTrigger.triggerSave("dashboard_service_link_deleted");
      res.json({ message: "Service link deleted" });
    } catch (err) {
      dashboardLogger.error("Failed to delete service link", err);
      res.status(500).json({ error: "Failed to delete service link" });
    }
  },
);

/**
 * @openapi
 * /service-links/{id}:
 *   put:
 *     summary: Update service link
 *     description: Updates label or url of a dashboard service link.
 *     tags:
 *       - Dashboard
 *     parameters:
 *       - in: path
 *         name: id
 *         required: true
 *         schema:
 *           type: integer
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             properties:
 *               label:
 *                 type: string
 *               url:
 *                 type: string
 *     responses:
 *       200:
 *         description: Service link updated.
 *       400:
 *         description: Invalid data.
 *       404:
 *         description: Not found.
 *       500:
 *         description: Failed to update service link.
 */
dashboardServiceLinksRouter.put("/:id", async (req: Request, res: Response) => {
  const userId = (req as AuthenticatedRequest).userId;
  const idParam = Array.isArray(req.params.id)
    ? req.params.id[0]
    : req.params.id;
  const id = parseInt(idParam);
  const { label, url } = req.body;

  if (isNaN(id)) {
    return res.status(400).json({ error: "Invalid id" });
  }
  if (url !== undefined && !isValidUrl(url)) {
    return res
      .status(400)
      .json({ error: "url must be a valid http or https URL" });
  }

  try {
    const existing =
      await createCurrentDashboardServiceLinkRepository().findByIdForUser(
        userId,
        id,
      );

    if (!existing) {
      return res.status(404).json({ error: "Not found" });
    }

    const updates: Partial<{ label: string; url: string }> = {};
    if (isNonEmptyString(label)) updates.label = label.trim();
    if (isNonEmptyString(url)) updates.url = url.trim();

    if (Object.keys(updates).length === 0) {
      return res.status(400).json({ error: "Nothing to update" });
    }

    const updated =
      await createCurrentDashboardServiceLinkRepository().updateForUser(
        userId,
        id,
        updates,
      );

    DatabaseSaveTrigger.triggerSave("dashboard_service_link_updated");
    res.json(updated);
  } catch (err) {
    dashboardLogger.error("Failed to update service link", err);
    res.status(500).json({ error: "Failed to update service link" });
  }
});
