import { Router } from "express";
import { prisma } from "../../db/prisma.js";
import { getVapidPublicKey } from "../../lib/push.js";
import { requireAuth, type AuthenticatedRequest } from "../../middleware/auth.js";
import {
  deactivateNativePushSubscriptionSchema,
  deactivatePushSubscriptionSchema,
  anonymousNativePushSubscriptionSchema,
  anonymousPushSubscriptionSchema,
  nativePushSubscriptionSchema,
  notificationSettingsSchema,
  pushSubscriptionSchema,
  replaceableAnonymousPushSubscriptionSchema,
  replaceablePushSubscriptionSchema
} from "./push.schemas.js";

export const pushRouter = Router();

function normalizeSubscriptionPayload(body: unknown) {
  if (!body || typeof body !== "object") {
    return body;
  }

  const payload = body as Record<string, unknown>;
  const nestedKeys =
    payload.keys && typeof payload.keys === "object" ? (payload.keys as Record<string, unknown>) : undefined;

  return {
    ...payload,
    keys: {
      p256dh: nestedKeys?.p256dh ?? payload.p256dh,
      auth: nestedKeys?.auth ?? payload.auth
    }
  };
}

async function getOrCreateAnonymousUser(clientId: string) {
  const email = `anon+${clientId}@push.local`;

  const user = await prisma.user.upsert({
    where: { email },
    update: { active: true },
    create: {
      email,
      passwordHash: "anonymous",
      role: "user",
      active: true,
      notificationSetting: {
        create: {
          signalsPushEnabled: true,
          resultsPushEnabled: false
        }
      }
    }
  });

  await prisma.notificationSetting.upsert({
    where: { userId: user.id },
    update: {
      signalsPushEnabled: true
    },
    create: {
      userId: user.id,
      signalsPushEnabled: true,
      resultsPushEnabled: false
    }
  });

  return user;
}

async function upsertNativePushSubscription(userId: string, payload: { token: string; platform: "android" | "ios"; deviceId?: string }) {
  return prisma.nativePushSubscription.upsert({
    where: {
      userId_token: {
        userId,
        token: payload.token
      }
    },
    update: {
      active: true,
      platform: payload.platform,
      deviceId: payload.deviceId
    },
    create: {
      userId,
      token: payload.token,
      platform: payload.platform,
      deviceId: payload.deviceId,
      active: true
    }
  });
}

pushRouter.get("/vapid-public-key", (_req, res) => {
  res.json({ publicKey: getVapidPublicKey() });
});

pushRouter.post("/public/push-subscriptions", async (req, res, next) => {
  try {
    const payload = replaceableAnonymousPushSubscriptionSchema.parse(normalizeSubscriptionPayload(req.body));
    const anonymousUser = await getOrCreateAnonymousUser(payload.clientId);

    const subscription = await prisma.$transaction(async (tx) => {
      if (payload.previousEndpoint && payload.previousEndpoint !== payload.endpoint) {
        await tx.pushSubscription.updateMany({
          where: {
            userId: anonymousUser.id,
            endpoint: payload.previousEndpoint
          },
          data: { active: false }
        });
      }

      return tx.pushSubscription.upsert({
        where: {
          userId_endpoint: {
            userId: anonymousUser.id,
            endpoint: payload.endpoint
          }
        },
        update: {
          active: true,
          p256dh: payload.keys.p256dh,
          auth: payload.keys.auth
        },
        create: {
          userId: anonymousUser.id,
          endpoint: payload.endpoint,
          p256dh: payload.keys.p256dh,
          auth: payload.keys.auth,
          active: true
        }
      });
    });

    res.status(201).json(subscription);
  } catch (error) {
    console.error("Public push subscription failed", {
      body: req.body,
      error
    });
    next(error);
  }
});

pushRouter.post("/public/native-push-subscriptions", async (req, res, next) => {
  try {
    const payload = anonymousNativePushSubscriptionSchema.parse(req.body);
    const anonymousUser = await getOrCreateAnonymousUser(payload.clientId);
    const subscription = await upsertNativePushSubscription(anonymousUser.id, payload);
    res.status(201).json(subscription);
  } catch (error) {
    console.error("Public native push subscription failed", {
      body: req.body,
      error
    });
    next(error);
  }
});

pushRouter.get("/public/push-subscriptions/:clientId", async (req, res, next) => {
  try {
    const payload = anonymousPushSubscriptionSchema.pick({ clientId: true }).parse(req.params);
    const email = `anon+${payload.clientId}@push.local`;

    const anonymousUser = await prisma.user.findUnique({
      where: { email },
      include: {
        pushSubscriptions: {
          where: { active: true },
          select: { id: true, endpoint: true, active: true, createdAt: true, updatedAt: true }
        }
      }
    });

    const items = anonymousUser?.pushSubscriptions ?? [];
    res.json({
      items,
      hasActiveSubscription: items.length > 0
    });
  } catch (error) {
    next(error);
  }
});

pushRouter.get("/public/native-push-subscriptions/:clientId", async (req, res, next) => {
  try {
    const payload = anonymousPushSubscriptionSchema.pick({ clientId: true }).parse(req.params);
    const email = `anon+${payload.clientId}@push.local`;

    const anonymousUser = await prisma.user.findUnique({
      where: { email },
      include: {
        nativePushSubscriptions: {
          where: { active: true },
          select: { id: true, platform: true, active: true, createdAt: true, updatedAt: true }
        }
      }
    });

    const items = anonymousUser?.nativePushSubscriptions ?? [];
    res.json({
      items,
      hasActiveSubscription: items.length > 0
    });
  } catch (error) {
    next(error);
  }
});

pushRouter.delete("/public/push-subscriptions/:clientId/:id", async (req, res, next) => {
  try {
    const payload = anonymousPushSubscriptionSchema.pick({ clientId: true }).parse(req.params);
    const email = `anon+${payload.clientId}@push.local`;
    const anonymousUser = await prisma.user.findUnique({ where: { email } });

    if (!anonymousUser) {
      return res.status(204).send();
    }

    await prisma.pushSubscription.updateMany({
      where: { id: String(req.params.id), userId: anonymousUser.id },
      data: { active: false }
    });

    res.status(204).send();
  } catch (error) {
    next(error);
  }
});

pushRouter.delete("/public/native-push-subscriptions/:clientId/:id", async (req, res, next) => {
  try {
    const payload = anonymousPushSubscriptionSchema.pick({ clientId: true }).parse(req.params);
    const email = `anon+${payload.clientId}@push.local`;
    const anonymousUser = await prisma.user.findUnique({ where: { email } });

    if (!anonymousUser) {
      return res.status(204).send();
    }

    await prisma.nativePushSubscription.updateMany({
      where: { id: String(req.params.id), userId: anonymousUser.id },
      data: { active: false }
    });

    res.status(204).send();
  } catch (error) {
    next(error);
  }
});

pushRouter.post("/me/push-subscriptions", requireAuth, async (req: AuthenticatedRequest, res, next) => {
  try {
    const payload = replaceablePushSubscriptionSchema.parse(normalizeSubscriptionPayload(req.body));
    const subscription = await prisma.$transaction(async (tx) => {
      if (payload.previousEndpoint && payload.previousEndpoint !== payload.endpoint) {
        await tx.pushSubscription.updateMany({
          where: {
            userId: req.user!.id,
            endpoint: payload.previousEndpoint
          },
          data: { active: false }
        });
      }

      return tx.pushSubscription.upsert({
        where: {
          userId_endpoint: {
            userId: req.user!.id,
            endpoint: payload.endpoint
          }
        },
        update: {
          active: true,
          p256dh: payload.keys.p256dh,
          auth: payload.keys.auth
        },
        create: {
          userId: req.user!.id,
          endpoint: payload.endpoint,
          p256dh: payload.keys.p256dh,
          auth: payload.keys.auth,
          active: true
        }
      });
    });

    res.status(201).json(subscription);
  } catch (error) {
    console.error("Authenticated push subscription failed", {
      userId: req.user?.id,
      body: req.body,
      error
    });
    next(error);
  }
});

pushRouter.post("/me/native-push-subscriptions", requireAuth, async (req: AuthenticatedRequest, res, next) => {
  try {
    const payload = nativePushSubscriptionSchema.parse(req.body);
    const subscription = await upsertNativePushSubscription(req.user!.id, payload);
    res.status(201).json(subscription);
  } catch (error) {
    console.error("Authenticated native push subscription failed", {
      userId: req.user?.id,
      body: req.body,
      error
    });
    next(error);
  }
});

pushRouter.get("/me/push-subscriptions", requireAuth, async (req: AuthenticatedRequest, res) => {
  const subscriptions = await prisma.pushSubscription.findMany({
    where: { userId: req.user!.id, active: true },
    select: {
      id: true,
      endpoint: true,
      active: true,
      createdAt: true,
      updatedAt: true
    }
  });

  res.json({
    items: subscriptions,
    hasActiveSubscription: subscriptions.length > 0
  });
});

pushRouter.get("/me/native-push-subscriptions", requireAuth, async (req: AuthenticatedRequest, res) => {
  const subscriptions = await prisma.nativePushSubscription.findMany({
    where: { userId: req.user!.id, active: true },
    select: {
      id: true,
      platform: true,
      active: true,
      createdAt: true,
      updatedAt: true
    }
  });

  res.json({
    items: subscriptions,
    hasActiveSubscription: subscriptions.length > 0
  });
});

pushRouter.delete("/me/push-subscriptions/:id", requireAuth, async (req: AuthenticatedRequest, res) => {
  await prisma.pushSubscription.updateMany({
    where: { id: String(req.params.id), userId: req.user!.id },
    data: { active: false }
  });
  res.status(204).send();
});

pushRouter.delete("/me/native-push-subscriptions/:id", requireAuth, async (req: AuthenticatedRequest, res) => {
  await prisma.nativePushSubscription.updateMany({
    where: { id: String(req.params.id), userId: req.user!.id },
    data: { active: false }
  });
  res.status(204).send();
});

pushRouter.post("/me/push-subscriptions/deactivate", requireAuth, async (req: AuthenticatedRequest, res, next) => {
  try {
    const payload = deactivatePushSubscriptionSchema.parse(req.body);

    await prisma.pushSubscription.updateMany({
      where: {
        userId: req.user!.id,
        endpoint: payload.endpoint,
        active: true
      },
      data: { active: false }
    });

    res.status(204).send();
  } catch (error) {
    next(error);
  }
});

pushRouter.post("/me/native-push-subscriptions/deactivate", requireAuth, async (req: AuthenticatedRequest, res, next) => {
  try {
    const payload = deactivateNativePushSubscriptionSchema.parse(req.body);

    await prisma.nativePushSubscription.updateMany({
      where: {
        userId: req.user!.id,
        token: payload.token,
        active: true
      },
      data: { active: false }
    });

    res.status(204).send();
  } catch (error) {
    next(error);
  }
});

pushRouter.get("/me/notification-settings", requireAuth, async (req: AuthenticatedRequest, res) => {
  const settings = await prisma.notificationSetting.findUnique({
    where: { userId: req.user!.id }
  });
  res.json(settings);
});

pushRouter.patch("/me/notification-settings", requireAuth, async (req: AuthenticatedRequest, res, next) => {
  try {
    const payload = notificationSettingsSchema.parse(req.body);
    const settings = await prisma.notificationSetting.upsert({
      where: { userId: req.user!.id },
      update: payload,
      create: {
        userId: req.user!.id,
        ...payload
      }
    });
    res.json(settings);
  } catch (error) {
    next(error);
  }
});
