import { Router } from "express";
import { SubscriptionStatus } from "@prisma/client";
import { prisma } from "../../db/prisma.js";
import { requireAdmin, requireAuth, type AuthenticatedRequest } from "../../middleware/auth.js";

export const usersRouter = Router();

function isSubscriptionActive(access: {
  status: SubscriptionStatus;
  startsAt: Date;
  expiresAt: Date | null;
}) {
  const now = Date.now();
  const startsAt = access.startsAt.getTime();
  const expiresAt = access.expiresAt?.getTime() ?? null;

  return access.status === SubscriptionStatus.active && startsAt <= now && (expiresAt === null || expiresAt >= now);
}

async function refreshExpiredSubscriptions(userId?: string) {
  await prisma.userBotAccess.updateMany({
    where: {
      ...(userId ? { userId } : {}),
      status: SubscriptionStatus.active,
      expiresAt: {
        lt: new Date()
      }
    },
    data: {
      status: SubscriptionStatus.expired
    }
  });
}

usersRouter.get("/admin/users", requireAuth, requireAdmin, async (req, res) => {
  await refreshExpiredSubscriptions();

  const query = String(req.query.q ?? "").trim();
  const where = {
    AND: [
      {
        NOT: {
          email: {
            startsWith: "anon+"
          }
        }
      },
      {
        NOT: {
          email: {
            endsWith: "@push.local"
          }
        }
      },
      ...(query
        ? [
            {
              email: {
                contains: query,
                mode: "insensitive" as const
              }
            }
          ]
        : [])
    ]
  };

  const users = await prisma.user.findMany({
    where,
    include: {
      pushSubscriptions: true,
      notificationSetting: true,
      botAccesses: {
        include: {
          bot: true
        },
        orderBy: [
          {
            status: "asc"
          },
          {
            createdAt: "desc"
          }
        ]
      }
    },
    orderBy: { createdAt: "desc" }
  });

  res.json(
    users.map((user) => ({
      ...user,
      botAccesses: user.botAccesses.map((access) => ({
        ...access,
        isActiveNow: isSubscriptionActive(access)
      }))
    }))
  );
});

usersRouter.patch("/admin/users/:id/active", requireAuth, requireAdmin, async (req, res) => {
  const user = await prisma.user.update({
    where: { id: String(req.params.id) },
    data: { active: Boolean(req.body.active) }
  });
  res.json(user);
});

usersRouter.get("/admin/bots", requireAuth, requireAdmin, async (_req, res) => {
  const bots = await prisma.bot.findMany({
    where: { active: true },
    orderBy: { name: "asc" }
  });

  res.json(bots);
});

usersRouter.put("/admin/users/:id/bot-access", requireAuth, requireAdmin, async (req: AuthenticatedRequest, res) => {
  const userId = String(req.params.id);
  const requestedBotIds = Array.isArray(req.body?.botIds) ? req.body.botIds.map((entry: unknown) => String(entry)) : [];

  const availableBots = await prisma.bot.findMany({
    where: {
      id: {
        in: requestedBotIds
      }
    },
    select: {
      id: true
    }
  });

  const validBotIds = availableBots.map((bot) => bot.id);

  await prisma.$transaction(async (tx) => {
    await tx.userBotAccess.updateMany({
      where: {
        userId,
        botId: {
          notIn: validBotIds.length > 0 ? validBotIds : ["__none__"]
        }
      },
      data: {
        status: SubscriptionStatus.canceled,
        expiresAt: new Date()
      }
    });

    for (const botId of validBotIds) {
      await tx.userBotAccess.upsert({
        where: {
          userId_botId: {
            userId,
            botId
          }
        },
        update: {
          status: SubscriptionStatus.active,
          startsAt: new Date(),
          expiresAt: null,
          notes: null,
          grantedById: req.user!.id
        },
        create: {
          userId,
          botId,
          grantedById: req.user!.id,
          status: SubscriptionStatus.active,
          startsAt: new Date()
        }
      });
    }
  });

  const updatedUser = await prisma.user.findUnique({
    where: { id: userId },
    include: {
      pushSubscriptions: true,
      notificationSetting: true,
      botAccesses: {
        include: {
          bot: true
        },
        orderBy: {
          bot: {
            name: "asc"
          }
        }
      }
    }
  });

  res.json(
    updatedUser
      ? {
          ...updatedUser,
          botAccesses: updatedUser.botAccesses.map((access) => ({
            ...access,
            isActiveNow: isSubscriptionActive(access)
          }))
        }
      : null
  );
});

usersRouter.patch("/admin/users/:id/subscriptions/:botId", requireAuth, requireAdmin, async (req: AuthenticatedRequest, res) => {
  const userId = String(req.params.id);
  const botId = String(req.params.botId);
  const status = String(req.body?.status ?? SubscriptionStatus.active) as SubscriptionStatus;
  const startsAt = req.body?.startsAt ? new Date(String(req.body.startsAt)) : new Date();
  const expiresAt = req.body?.expiresAt ? new Date(String(req.body.expiresAt)) : null;
  const notes = typeof req.body?.notes === "string" ? req.body.notes.trim() || null : null;

  if (!Object.values(SubscriptionStatus).includes(status)) {
    return res.status(400).json({ message: "Некорректный статус подписки" });
  }

  const bot = await prisma.bot.findUnique({
    where: { id: botId },
    select: { id: true }
  });

  if (!bot) {
    return res.status(404).json({ message: "Бот не найден" });
  }

  if (Number.isNaN(startsAt.getTime()) || (expiresAt && Number.isNaN(expiresAt.getTime()))) {
    return res.status(400).json({ message: "Некорректная дата подписки" });
  }

  if (expiresAt && expiresAt < startsAt) {
    return res.status(400).json({ message: "Дата окончания не может быть раньше даты начала" });
  }

  const access = await prisma.userBotAccess.upsert({
    where: {
      userId_botId: {
        userId,
        botId
      }
    },
    update: {
      status,
      startsAt,
      expiresAt,
      notes,
      grantedById: req.user!.id,
      grantedAt: new Date()
    },
    create: {
      userId,
      botId,
      status,
      startsAt,
      expiresAt,
      notes,
      grantedById: req.user!.id
    },
    include: {
      bot: true
    }
  });

  res.json({
    ...access,
    isActiveNow: isSubscriptionActive(access)
  });
});

usersRouter.get("/me/subscriptions", requireAuth, async (req: AuthenticatedRequest, res) => {
  await refreshExpiredSubscriptions(req.user!.id);

  const subscriptions = await prisma.userBotAccess.findMany({
    where: {
      userId: req.user!.id
    },
    include: {
      bot: true
    },
    orderBy: [
      {
        status: "asc"
      },
      {
        startsAt: "desc"
      }
    ]
  });

  res.json(
    subscriptions.map((subscription) => ({
      ...subscription,
      isActiveNow: isSubscriptionActive(subscription)
    }))
  );
});
