import { eq, and } from "drizzle-orm";
import { TRPCError } from "@trpc/server";
import { db } from "~/server/db";
import { deviceTokens } from "~/server/db/schema/auth";
import { notificationPreferences } from "~/server/db/schema/notifications";
import { resend } from "~/server/lib/resend";
import { messaging } from "~/server/lib/fcm";
import { twilioClient } from "~/server/lib/twilio";

export async function sendEmail(
  to: string,
  subject: string,
  html: string,
  text?: string,
) {
  if (!process.env.RESEND_API_KEY) {
    console.warn("[notifications] Resend not configured, skipping email");
    return { id: null };
  }

  try {
    const { data, error } = await resend.emails.send({
      from: process.env.RESEND_FROM_EMAIL ?? "noreply@shieldai.app",
      to,
      subject,
      html,
      text: text ?? "",
    });

    if (error) {
      console.error("[notifications] Resend error:", error);
      throw new TRPCError({
        code: "INTERNAL_SERVER_ERROR",
        message: "Failed to send email",
      });
    }

    console.log("[notifications] Email sent:", data?.id);
    return { id: data?.id ?? null };
  } catch (err) {
    if (err instanceof TRPCError) throw err;
    console.error("[notifications] Email send error:", err);
    throw new TRPCError({
      code: "INTERNAL_SERVER_ERROR",
      message: "Failed to send email",
    });
  }
}

export async function sendPush(
  userId: string,
  title: string,
  body: string,
  data?: Record<string, string>,
) {
  const tokens = await db
    .select()
    .from(deviceTokens)
    .where(
      and(
        eq(deviceTokens.userId, userId),
        eq(deviceTokens.isActive, true),
      ),
    );

  if (!tokens.length) {
    console.warn("[notifications] No active devices for user", userId);
    return { successCount: 0 };
  }

  if (!messaging) {
    console.warn("[notifications] FCM not configured, skipping push");
    return { successCount: 0 };
  }

  const tokenStrings = tokens.map((t) => t.token);
  let successCount = 0;

  for (const token of tokenStrings) {
    try {
      await messaging.send({
        token,
        notification: { title, body },
        data,
        android: { priority: "high" },
        apns: {
          payload: {
            aps: {
              alert: { title, body },
              sound: "default",
              badge: 1,
            },
          },
        },
      });
      successCount++;
    } catch (err) {
      console.error("[notifications] Push send error for token:", err);
    }
  }

  console.log("[notifications] Push sent to", successCount, "/", tokens.length, "devices");
  return { successCount };
}

export async function sendSMS(phoneNumber: string, message: string) {
  const e164Regex = /^\+[1-9]\d{6,14}$/;
  if (!e164Regex.test(phoneNumber)) {
    throw new TRPCError({
      code: "BAD_REQUEST",
      message: "Phone number must be in E.164 format (e.g. +1234567890)",
    });
  }

  if (!twilioClient) {
    console.warn("[notifications] Twilio not configured, skipping SMS");
    return { sid: null };
  }

  try {
    const result = await twilioClient.messages.create({
      body: message,
      to: phoneNumber,
      messagingServiceSid: process.env.TWILIO_MESSAGING_SERVICE_SID,
    });

    console.log("[notifications] SMS sent:", result.sid);
    return { sid: result.sid };
  } catch (err) {
    console.error("[notifications] SMS send error:", err);
    throw new TRPCError({
      code: "INTERNAL_SERVER_ERROR",
      message: "Failed to send SMS",
    });
  }
}

export async function registerDevice(
  userId: string,
  token: string,
  platform: "ios" | "android" | "web",
  deviceType: "mobile" | "web" | "desktop",
) {
  const [existing] = await db
    .select()
    .from(deviceTokens)
    .where(eq(deviceTokens.token, token))
    .limit(1);

  if (existing) {
    if (existing.userId !== userId) {
      throw new TRPCError({
        code: "CONFLICT",
        message: "Device token already registered to another user",
      });
    }

    const [updated] = await db
      .update(deviceTokens)
      .set({ isActive: true, lastUsedAt: new Date() })
      .where(eq(deviceTokens.id, existing.id))
      .returning();

    return updated;
  }

  const [created] = await db
    .insert(deviceTokens)
    .values({ userId, token, platform, deviceType })
    .returning();

  return created;
}

export async function unregisterDevice(userId: string, token: string) {
  const [existing] = await db
    .select()
    .from(deviceTokens)
    .where(
      and(
        eq(deviceTokens.token, token),
        eq(deviceTokens.userId, userId),
      ),
    )
    .limit(1);

  if (!existing) {
    throw new TRPCError({
      code: "NOT_FOUND",
      message: "Device token not found",
    });
  }

  const [updated] = await db
    .update(deviceTokens)
    .set({ isActive: false })
    .where(eq(deviceTokens.id, existing.id))
    .returning();

  return updated;
}

export async function listDevices(userId: string) {
  const devices = await db
    .select()
    .from(deviceTokens)
    .where(eq(deviceTokens.userId, userId))
    .orderBy(deviceTokens.createdAt);

  return devices;
}

export async function getPreferences(userId: string) {
  const [prefs] = await db
    .select()
    .from(notificationPreferences)
    .where(eq(notificationPreferences.userId, userId))
    .limit(1);

  if (!prefs) {
    return {
      emailEnabled: true,
      pushEnabled: true,
      smsEnabled: true,
    };
  }

  return prefs;
}

export async function updatePreferences(
  userId: string,
  prefs: { emailEnabled?: boolean; pushEnabled?: boolean; smsEnabled?: boolean },
) {
  const [existing] = await db
    .select()
    .from(notificationPreferences)
    .where(eq(notificationPreferences.userId, userId))
    .limit(1);

  if (existing) {
    const [updated] = await db
      .update(notificationPreferences)
      .set(prefs)
      .where(eq(notificationPreferences.userId, userId))
      .returning();
    return updated;
  }

  const [created] = await db
    .insert(notificationPreferences)
    .values({ userId, ...prefs })
    .returning();

  return created;
}
