import type { FastifyInstance } from "fastify";
import { z } from "zod";

import { AppError } from "../lib/errors.js";
import { supabaseAdmin } from "../lib/supabase.js";
import { isoNow } from "../lib/utils.js";
import { requirePlatformAdmin } from "../services/auth-service.js";

function startOfDay(date: Date) {
  return new Date(date.getFullYear(), date.getMonth(), date.getDate());
}

function isoDate(date: Date) {
  return date.toISOString().slice(0, 10);
}

export function registerAdminRoutes(app: FastifyInstance) {
  app.get("/api/admin/overview", async (request) => {
    await requirePlatformAdmin(request);

    const now = new Date();
    const thirtyDaysAgo = new Date(now);
    thirtyDaysAgo.setDate(now.getDate() - 29);
    const sevenDaysAgo = new Date(now);
    sevenDaysAgo.setDate(now.getDate() - 6);
    const oneDayAgo = new Date(now);
    oneDayAgo.setDate(now.getDate() - 1);

    const [
      { count: userCount, error: userCountError },
      { count: organizationCount, error: organizationCountError },
      { count: contactCount, error: contactCountError },
      { count: campaignCount, error: campaignCountError },
      { count: sendCount, error: sendCountError },
      { data: activityCampaigns, error: campaignActivityError },
      { data: activityContacts, error: contactActivityError },
      { data: newUsers, error: newUsersError },
      { data: usageRows, error: usageError },
      { data: subscriptions, error: subscriptionsError },
      { data: plans, error: plansError },
    ] = await Promise.all([
      supabaseAdmin.from("user_profiles").select("auth_user_id", { count: "exact", head: true }),
      supabaseAdmin.from("organizations").select("id", { count: "exact", head: true }),
      supabaseAdmin.from("contacts").select("id", { count: "exact", head: true }),
      supabaseAdmin.from("campaigns").select("id", { count: "exact", head: true }),
      supabaseAdmin.from("campaign_sends").select("id", { count: "exact", head: true }),
      supabaseAdmin
        .from("campaign_activity_logs")
        .select("actor_auth_user_id, created_at")
        .gte("created_at", startOfDay(thirtyDaysAgo).toISOString()),
      supabaseAdmin
        .from("contact_activity_logs")
        .select("actor_auth_user_id, created_at")
        .gte("created_at", startOfDay(thirtyDaysAgo).toISOString()),
      supabaseAdmin
        .from("user_profiles")
        .select("auth_user_id, created_at")
        .gte("created_at", startOfDay(thirtyDaysAgo).toISOString()),
      supabaseAdmin
        .from("monthly_usage_counters")
        .select("organization_id, period_start, contacts_count, emails_sent_count")
        .gte("period_start", isoDate(new Date(now.getFullYear(), now.getMonth() - 5, 1)))
        .order("period_start", { ascending: true }),
      supabaseAdmin
        .from("organization_subscriptions")
        .select("organization_id, plan_definition_id, status"),
      supabaseAdmin
        .from("plan_definitions")
        .select("id, code, name, price_cents, contact_limit, monthly_email_limit, description, is_active")
        .order("price_cents", { ascending: true }),
    ]);

    for (const error of [userCountError, organizationCountError, contactCountError, campaignCountError, sendCountError, campaignActivityError, contactActivityError, newUsersError, usageError, subscriptionsError, plansError]) {
      if (error) {
        throw new AppError(400, "ADMIN_OVERVIEW_FAILED", error.message);
      }
    }

    const allActivities = [...(activityCampaigns ?? []), ...(activityContacts ?? [])]
      .map((row: any) => ({
        actorAuthUserId: String(row.actor_auth_user_id ?? "").trim(),
        createdAt: String(row.created_at ?? ""),
      }))
      .filter((row) => row.actorAuthUserId && row.createdAt);

    const activeUsersInWindow = (start: Date) =>
      new Set(
        allActivities
          .filter((row) => new Date(row.createdAt).getTime() >= start.getTime())
          .map((row) => row.actorAuthUserId)
      ).size;

    const activityByDay = new Map<string, Set<string>>();
    for (let index = 0; index < 30; index += 1) {
      const date = new Date(thirtyDaysAgo);
      date.setDate(thirtyDaysAgo.getDate() + index);
      activityByDay.set(isoDate(date), new Set());
    }

    for (const row of allActivities) {
      const key = row.createdAt.slice(0, 10);
      activityByDay.get(key)?.add(row.actorAuthUserId);
    }

    const newUsersByDay = new Map<string, number>();
    for (let index = 0; index < 30; index += 1) {
      const date = new Date(thirtyDaysAgo);
      date.setDate(thirtyDaysAgo.getDate() + index);
      newUsersByDay.set(isoDate(date), 0);
    }
    for (const row of newUsers ?? []) {
      const key = String(row.created_at ?? "").slice(0, 10);
      newUsersByDay.set(key, (newUsersByDay.get(key) ?? 0) + 1);
    }

    const plansById = new Map(((plans ?? []) as Array<Record<string, any>>).map((plan) => [String(plan.id), plan]));
    const subscriptionMix = ((subscriptions ?? []) as Array<Record<string, any>>).reduce<Record<string, number>>((acc, row) => {
      const plan = plansById.get(String(row.plan_definition_id ?? ""));
      const key = String(plan?.code ?? "unknown");
      acc[key] = (acc[key] ?? 0) + 1;
      return acc;
    }, {});

    return {
      totals: {
        users: userCount ?? 0,
        organizations: organizationCount ?? 0,
        contacts: contactCount ?? 0,
        campaigns: campaignCount ?? 0,
        sends: sendCount ?? 0,
      },
      activeUsers: {
        dau: activeUsersInWindow(startOfDay(oneDayAgo)),
        wau: activeUsersInWindow(startOfDay(sevenDaysAgo)),
        mau: activeUsersInWindow(startOfDay(thirtyDaysAgo)),
      },
      charts: {
        activeUsers30d: Array.from(activityByDay.entries()).map(([date, actors]) => ({ date, value: actors.size })),
        newUsers30d: Array.from(newUsersByDay.entries()).map(([date, value]) => ({ date, value })),
        usageByMonth: (usageRows ?? []).map((row: any) => ({
          month: String(row.period_start ?? "").slice(0, 7),
          contacts: Number(row.contacts_count ?? 0),
          emailsSent: Number(row.emails_sent_count ?? 0),
        })),
      },
      subscriptionMix,
      plans: plans ?? [],
    };
  });

  app.get("/api/admin/users", async (request) => {
    await requirePlatformAdmin(request);

    const [{ data: users, error: usersError }, { data: memberships, error: membershipsError }, { data: organizations, error: organizationsError }] = await Promise.all([
      supabaseAdmin
        .from("user_profiles")
        .select("auth_user_id, email, full_name, role, default_organization_id, created_at, updated_at, onboarding_completed_at")
        .order("created_at", { ascending: false }),
      supabaseAdmin
        .from("organization_members")
        .select("auth_user_id, organization_id, role"),
      supabaseAdmin
        .from("organizations")
        .select("id, name, slug"),
    ]);

    for (const error of [usersError, membershipsError, organizationsError]) {
      if (error) {
        throw new AppError(400, "ADMIN_USERS_FAILED", error.message);
      }
    }

    const organizationsById = new Map(((organizations ?? []) as Array<Record<string, any>>).map((organization) => [String(organization.id), organization]));
    const membershipsByUser = new Map<string, Array<Record<string, any>>>();

    for (const membership of (memberships ?? []) as Array<Record<string, any>>) {
      const authUserId = String(membership.auth_user_id ?? "");
      if (!authUserId) continue;
      if (!membershipsByUser.has(authUserId)) membershipsByUser.set(authUserId, []);
      membershipsByUser.get(authUserId)?.push(membership);
    }

    return {
      items: ((users ?? []) as Array<Record<string, any>>).map((user) => {
        const authUserId = String(user.auth_user_id ?? "");
        const userMemberships = membershipsByUser.get(authUserId) ?? [];
        return {
          authUserId,
          email: String(user.email ?? ""),
          fullName: String(user.full_name ?? ""),
          role: String(user.role ?? "user"),
          createdAt: user.created_at ?? null,
          updatedAt: user.updated_at ?? null,
          onboardingCompletedAt: user.onboarding_completed_at ?? null,
          defaultOrganizationId: user.default_organization_id ?? null,
          organizations: userMemberships.map((membership) => {
            const organization = organizationsById.get(String(membership.organization_id ?? ""));
            return {
              id: organization?.id ?? membership.organization_id ?? null,
              name: organization?.name ?? "Unknown organization",
              slug: organization?.slug ?? "",
              role: String(membership.role ?? "member"),
            };
          }),
        };
      }),
    };
  });

  app.get("/api/admin/plans", async (request) => {
    await requirePlatformAdmin(request);

    const { data, error } = await supabaseAdmin
      .from("plan_definitions")
      .select("id, code, name, price_cents, contact_limit, monthly_email_limit, description, is_active")
      .order("price_cents", { ascending: true });

    if (error) {
      throw new AppError(400, "ADMIN_PLANS_FAILED", error.message);
    }

    return { items: data ?? [] };
  });

  app.put("/api/admin/plans/:code", async (request) => {
    const params = z.object({ code: z.string().min(1) }).parse(request.params);
    const payload = z.object({
      name: z.string().min(1),
      priceCents: z.number().int().min(0),
      contactLimit: z.number().int().positive().nullable().optional(),
      monthlyEmailLimit: z.number().int().positive().nullable().optional(),
      description: z.string().nullable().optional(),
      isActive: z.boolean().default(true),
    }).parse(request.body);

    await requirePlatformAdmin(request);

    const { data, error } = await supabaseAdmin
      .from("plan_definitions")
      .update({
        name: payload.name,
        price_cents: payload.priceCents,
        contact_limit: payload.contactLimit ?? null,
        monthly_email_limit: payload.monthlyEmailLimit ?? null,
        description: payload.description ?? null,
        is_active: payload.isActive,
        updated_at: isoNow(),
      })
      .eq("code", params.code)
      .select("id, code, name, price_cents, contact_limit, monthly_email_limit, description, is_active")
      .single();

    if (error || !data) {
      throw new AppError(400, "ADMIN_PLAN_UPDATE_FAILED", error?.message ?? "Unable to update plan.");
    }

    return { plan: data };
  });

  app.get("/api/admin/content", async (request) => {
    await requirePlatformAdmin(request);

    const { data, error } = await supabaseAdmin
      .from("admin_content_pages")
      .select("*")
      .order("slug", { ascending: true });

    if (error) {
      throw new AppError(400, "ADMIN_CONTENT_LIST_FAILED", error.message);
    }

    return {
      pages: data ?? []
    };
  });

  app.get("/api/admin/content/:slug", async (request) => {
    const params = z.object({ slug: z.string().min(1) }).parse(request.params);
    await requirePlatformAdmin(request);

    const { data, error } = await supabaseAdmin
      .from("admin_content_pages")
      .select("*")
      .eq("slug", params.slug)
      .maybeSingle();

    if (error) {
      throw new AppError(400, "ADMIN_CONTENT_LOOKUP_FAILED", error.message);
    }

    return {
      page: data
    };
  });

  app.put("/api/admin/content/:slug", async (request) => {
    const params = z.object({ slug: z.string().min(1) }).parse(request.params);
    const payload = z
      .object({
        title: z.string().min(1),
        body: z.any(),
        published: z.boolean().default(true)
      })
      .parse(request.body);

    await requirePlatformAdmin(request);

    const { data, error } = await supabaseAdmin
      .from("admin_content_pages")
      .upsert(
        {
          slug: params.slug,
          title: payload.title,
          body: payload.body,
          published: payload.published,
          updated_at: isoNow()
        },
        {
          onConflict: "slug"
        }
      )
      .select("*")
      .single();

    if (error) {
      throw new AppError(400, "ADMIN_CONTENT_UPDATE_FAILED", error.message);
    }

    return {
      page: data
    };
  });

  app.get("/api/admin/settings", async (request) => {
    await requirePlatformAdmin(request);
    const { data } = await supabaseAdmin.from("admin_settings").select("*").order("key", { ascending: true });
    return {
      items: data ?? []
    };
  });

  app.put("/api/admin/settings/:key", async (request) => {
    const params = z.object({ key: z.string().min(1) }).parse(request.params);
    const payload = z.object({ value: z.any() }).parse(request.body);
    await requirePlatformAdmin(request);

    const { data, error } = await supabaseAdmin
      .from("admin_settings")
      .upsert(
        {
          key: params.key,
          value: payload.value,
          updated_at: isoNow()
        },
        {
          onConflict: "key"
        }
      )
      .select("*")
      .single();

    if (error) {
      throw new AppError(400, "ADMIN_SETTING_UPDATE_FAILED", error.message);
    }

    return {
      setting: data
    };
  });
}
