import { Router } from "express";
import { Prisma, SignalStatus } from "@prisma/client";
import { prisma } from "../../db/prisma.js";
import { HttpError } from "../../lib/errors.js";
import { requireAdmin, requireAuth, type AuthenticatedRequest } from "../../middleware/auth.js";
import { enqueueBroadcastPush, enqueueSignalPush } from "../queues/push.queue.js";
import { signalCreateSchema, signalStatusSchema, signalUpdateSchema } from "../signals/signals.schemas.js";
import { createSignal, updateSignal } from "../signals/signals.service.js";

export const adminRouter = Router();
adminRouter.use(requireAuth, requireAdmin);

type PushEventPayload = {
  subscriptionId?: string;
  endpoint?: string;
  endpointHost?: string;
  ok?: boolean;
  statusCode?: number | null;
  reason?: string | null;
  notificationType?: string | null;
};

async function logAdminAction(
  adminId: string,
  action: string,
  entityType: string,
  entityId: string,
  metadata?: Record<string, unknown>
) {
  await prisma.adminActionLog.create({
    data: {
      adminId,
      action,
      entityType,
      entityId,
      metadata: metadata ? (metadata as Prisma.InputJsonObject) : undefined
    }
  });
}

adminRouter.post("/signals", async (req: AuthenticatedRequest, res, next) => {
  try {
    const payload = signalCreateSchema.parse(req.body);
    const signal = await createSignal(payload);
    await logAdminAction(req.user!.id, "create", "signal", signal.id);
    res.status(201).json(signal);
  } catch (error) {
    next(error);
  }
});

adminRouter.patch("/signals/:id", async (req: AuthenticatedRequest, res, next) => {
  try {
    const payload = signalUpdateSchema.parse(req.body);
    const signal = await updateSignal(String(req.params.id), payload);
    await logAdminAction(req.user!.id, "update", "signal", signal.id, payload);
    res.json(signal);
  } catch (error) {
    next(error);
  }
});

adminRouter.delete("/signals/:id", async (req: AuthenticatedRequest, res) => {
  await prisma.signal.delete({ where: { id: String(req.params.id) } });
  await logAdminAction(req.user!.id, "delete", "signal", String(req.params.id));
  res.status(204).send();
});

adminRouter.post("/signals/:id/duplicate", async (req: AuthenticatedRequest, res, next) => {
  try {
    const source = await prisma.signal.findUnique({ where: { id: String(req.params.id) } });
    if (!source) {
      throw new HttpError(404, "Сигнал не найден");
    }

    const duplicate = await createSignal({
      providerId: source.providerId,
      eventId: `${source.eventId}-${Date.now()}`,
      sportType: source.sportType,
      leagueName: source.leagueName,
      homeTeam: source.homeTeam,
      awayTeam: source.awayTeam,
      eventStartTime: source.eventStartTime.toISOString(),
      marketType: source.marketType,
      selection: source.selection,
      lineValue: source.lineValue,
      odds: source.odds,
      signalTime: new Date().toISOString(),
      sourceType: source.sourceType,
      comment: source.comment,
      rawPayload: source.rawPayload as Record<string, unknown> | null,
      published: false
    });

    await logAdminAction(req.user!.id, "duplicate", "signal", duplicate.id, { sourceId: source.id });
    res.status(201).json(duplicate);
  } catch (error) {
    next(error);
  }
});

adminRouter.post("/signals/:id/send-push", async (req: AuthenticatedRequest, res, next) => {
  try {
    const job = await enqueueSignalPush({
      type: "signal",
      signalId: String(req.params.id),
      force: true
    });
    await logAdminAction(req.user!.id, "send_push", "signal", String(req.params.id), {
      queued: true,
      jobId: job.id
    });
    res.status(202).json({ queued: true, jobId: job.id });
  } catch (error) {
    next(error);
  }
});

adminRouter.post("/signals/:id/set-status", async (req: AuthenticatedRequest, res, next) => {
  try {
    const payload = signalStatusSchema.parse(req.body);
    await prisma.signal.update({
      where: { id: String(req.params.id) },
      data: { status: payload.status as SignalStatus }
    });

    const settlement = await prisma.settlement.upsert({
      where: { signalId: String(req.params.id) },
      update: { result: payload.status as SignalStatus, explanation: payload.explanation },
      create: {
        signalId: String(req.params.id),
        result: payload.status as SignalStatus,
        explanation: payload.explanation
      }
    });

    await logAdminAction(req.user!.id, "set_status", "signal", String(req.params.id), payload);
    res.json(settlement);
  } catch (error) {
    next(error);
  }
});

adminRouter.post("/broadcast", async (req: AuthenticatedRequest, res, next) => {
  try {
    const title = String(req.body.title ?? "").trim();
    const body = String(req.body.body ?? "").trim();
    if (!title || !body) {
      throw new HttpError(400, "Нужны title и body");
    }

    const job = await enqueueBroadcastPush({
      type: "broadcast",
      title,
      body,
      url: req.body.url ? String(req.body.url) : "/",
      tag: req.body.tag ? String(req.body.tag) : undefined,
      renotify: Boolean(req.body.renotify)
    });

    await logAdminAction(req.user!.id, "broadcast", "notification", "broadcast", {
      title,
      body,
      url: req.body.url,
      tag: req.body.tag,
      renotify: Boolean(req.body.renotify),
      queued: true,
      jobId: job.id
    });

    res.status(202).json({ queued: true, jobId: job.id });
  } catch (error) {
    next(error);
  }
});

adminRouter.get("/logs", async (_req, res) => {
  const [pushLogs, adminActions, integrationLogs] = await Promise.all([
    prisma.notificationLog.findMany({ orderBy: { createdAt: "desc" }, take: 50 }),
    prisma.adminActionLog.findMany({ orderBy: { createdAt: "desc" }, take: 50, include: { admin: true } }),
    prisma.integrationLog.findMany({ orderBy: { createdAt: "desc" }, take: 50 })
  ]);

  res.json({ pushLogs, adminActions, integrationLogs });
});

adminRouter.get("/push-subscriptions", async (_req, res) => {
  const [subscriptions, recentEvents, notificationLogs] = await Promise.all([
    prisma.pushSubscription.findMany({
      orderBy: { updatedAt: "desc" },
      include: {
        user: {
          select: {
            id: true,
            email: true,
            role: true,
            active: true,
            notificationSetting: true
          }
        }
      }
    }),
    prisma.integrationLog.findMany({
      where: { provider: "web-push" },
      orderBy: { createdAt: "desc" },
      take: 500
    }),
    prisma.notificationLog.findMany({
      orderBy: { createdAt: "desc" },
      take: 20
    })
  ]);

  const latestEventBySubscription = new Map<string, (typeof recentEvents)[number]>();

  for (const event of recentEvents) {
    const payload =
      event.payload && typeof event.payload === "object" ? (event.payload as unknown as PushEventPayload) : null;
    const subscriptionId = payload?.subscriptionId;

    if (!subscriptionId || latestEventBySubscription.has(subscriptionId)) {
      continue;
    }

    latestEventBySubscription.set(subscriptionId, event);
  }

  const items = subscriptions.map((subscription) => {
    const latestEvent = latestEventBySubscription.get(subscription.id);
    const payload =
      latestEvent?.payload && typeof latestEvent.payload === "object"
        ? (latestEvent.payload as unknown as PushEventPayload)
        : null;

    const status = !subscription.active
      ? "inactive"
      : payload?.ok === false
        ? "error"
        : payload?.ok === true
          ? "ok"
          : "ready";

    return {
      id: subscription.id,
      endpoint: subscription.endpoint,
      endpointHost: payload?.endpointHost ?? (() => {
        try {
          return new URL(subscription.endpoint).hostname;
        } catch {
          return "unknown";
        }
      })(),
      active: subscription.active,
      createdAt: subscription.createdAt,
      updatedAt: subscription.updatedAt,
      status,
      user: {
        id: subscription.user.id,
        email: subscription.user.email,
        role: subscription.user.role,
        active: subscription.user.active,
        notificationSetting: subscription.user.notificationSetting
      },
      latestEvent: latestEvent
        ? {
            createdAt: latestEvent.createdAt,
            level: latestEvent.level,
            message: latestEvent.message,
            ok: payload?.ok ?? null,
            statusCode: payload?.statusCode ?? null,
            reason: payload?.reason ?? null,
            notificationType: payload?.notificationType ?? null
          }
        : null
    };
  });

  const summary = {
    total: items.length,
    active: items.filter((item) => item.active).length,
    inactive: items.filter((item) => !item.active).length,
    ok: items.filter((item) => item.status === "ok").length,
    ready: items.filter((item) => item.status === "ready").length,
    error: items.filter((item) => item.status === "error").length
  };

  res.json({
    summary,
    items,
    recentNotificationLogs: notificationLogs
  });
});

adminRouter.post("/event-results", async (req: AuthenticatedRequest, res) => {
  const result = await prisma.eventResult.upsert({
    where: { eventId: req.body.eventId },
    update: {
      homeScore: req.body.homeScore,
      awayScore: req.body.awayScore,
      status: req.body.status ?? "finished",
      payload: req.body.payload ?? null
    },
    create: {
      eventId: req.body.eventId,
      homeScore: req.body.homeScore,
      awayScore: req.body.awayScore,
      status: req.body.status ?? "finished",
      payload: req.body.payload ?? null
    }
  });

  await logAdminAction(req.user!.id, "upsert_event_result", "event_result", result.id, req.body);
  res.status(201).json(result);
});
