import { createServerFn } from "@tanstack/react-start";
import { createClient } from "@supabase/supabase-js";
import { z } from "zod";
import type { Database } from "@/integrations/supabase/types";
import { requireSupabaseAuth } from "@/integrations/supabase/auth-middleware";

export interface EpisodeRow {
  id: string;
  slug: string;
  title: string;
  summary: string;
  show_notes: string;
  video_url: string | null;
  audio_url: string | null;
  cover_image_url: string | null;
  duration_minutes: number;
  published: boolean;
  published_at: string;
}

export interface ScheduleRow {
  id: string;
  title: string;
  topic: string;
  starts_at: string;
  duration_minutes: number;
  status: string;
  stream_url: string | null;
  published: boolean;
}

export interface GuestRow {
  id: string;
  name: string;
  role_title: string;
  bio: string;
  avatar_url: string | null;
  link_url: string | null;
  episode_id: string | null;
  published: boolean;
}

export interface QuestionRow {
  id: string;
  episode_id: string | null;
  display_name: string;
  question: string;
  answer: string | null;
  status: string;
  created_at: string;
}

function publicClient() {
  const key = process.env.SUPABASE_PUBLISHABLE_KEY!;
  return createClient<Database>(process.env.SUPABASE_URL!, key, {
    auth: { persistSession: false, autoRefreshToken: false },
    global: {
      fetch: (input, init) => {
        const h = new Headers(init?.headers);
        if (key.startsWith("sb_") && h.get("Authorization") === `Bearer ${key}`) {
          h.delete("Authorization");
        }
        h.set("apikey", key);
        return fetch(input, { ...init, headers: h });
      },
    },
  });
}

async function assertAdmin(context: { supabase: any; userId: string }) {
  const { data, error } = await context.supabase.rpc("has_role", {
    _user_id: context.userId,
    _role: "admin",
  });
  if (error) throw new Error("Could not verify your permissions.");
  if (!data) throw new Error("Forbidden: admin access required.");
}

/* ---------------- public reads ---------------- */

export const listSchedule = createServerFn({ method: "GET" }).handler(async () => {
  const { data, error } = await publicClient()
    .from("podcast_schedule")
    .select("*")
    .eq("published", true)
    .order("starts_at", { ascending: true });
  if (error) {
    console.error("listSchedule failed", error);
    return [] as ScheduleRow[];
  }
  return (data ?? []) as ScheduleRow[];
});

export const listEpisodes = createServerFn({ method: "GET" }).handler(async () => {
  const { data, error } = await publicClient()
    .from("podcast_episodes")
    .select("*")
    .eq("published", true)
    .order("published_at", { ascending: false });
  if (error) {
    console.error("listEpisodes failed", error);
    return [] as EpisodeRow[];
  }
  return (data ?? []) as EpisodeRow[];
});

export const getEpisode = createServerFn({ method: "GET" })
  .inputValidator((slug: string) => String(slug).slice(0, 200))
  .handler(async ({ data: slug }) => {
    const client = publicClient();
    const { data, error } = await client
      .from("podcast_episodes")
      .select("*")
      .eq("slug", slug)
      .eq("published", true)
      .maybeSingle();
    if (error || !data) return null;
    const episode = data as EpisodeRow;
    const { data: guests } = await client
      .from("podcast_guests")
      .select("*")
      .eq("episode_id", episode.id)
      .eq("published", true);
    return { episode, guests: (guests ?? []) as GuestRow[] };
  });

export const listGuests = createServerFn({ method: "GET" }).handler(async () => {
  const { data, error } = await publicClient()
    .from("podcast_guests")
    .select("*")
    .eq("published", true)
    .order("created_at", { ascending: false });
  if (error) return [] as GuestRow[];
  return (data ?? []) as GuestRow[];
});

export const listApprovedQuestions = createServerFn({ method: "GET" }).handler(async () => {
  const { data, error } = await publicClient()
    .from("podcast_questions")
    .select("*")
    .in("status", ["approved", "answered"])
    .order("created_at", { ascending: false })
    .limit(60);
  if (error) return [] as QuestionRow[];
  return (data ?? []) as QuestionRow[];
});

/* ---------------- public writes ---------------- */

const questionSchema = z.object({
  displayName: z.string().trim().min(2).max(60),
  question: z.string().trim().min(5).max(600),
  website: z.string().max(200).optional(),
});

export const submitQuestion = createServerFn({ method: "POST" })
  .inputValidator((input: unknown) => questionSchema.parse(input))
  .handler(async ({ data }) => {
    if (data.website) return { ok: true as const, moderated: true as const };
    const { error } = await publicClient().from("podcast_questions").insert({
      display_name: data.displayName,
      question: data.question,
      status: "pending",
    });
    if (error) {
      console.error("submitQuestion failed", error);
      throw new Error("We could not send your question. Please try again.");
    }
    return { ok: true as const, moderated: true as const };
  });

const subscribeSchema = z.object({
  email: z.string().trim().email().max(200),
  browserPush: z.boolean().default(false),
});

export const subscribeGoLive = createServerFn({ method: "POST" })
  .inputValidator((input: unknown) => subscribeSchema.parse(input))
  .handler(async ({ data }) => {
    const { supabaseAdmin } = await import("@/integrations/supabase/client.server");
    const { error } = await supabaseAdmin
      .from("podcast_subscribers")
      .upsert(
        { email: data.email.toLowerCase(), browser_push: data.browserPush },
        { onConflict: "email" },
      );
    if (error) {
      console.error("subscribeGoLive failed", error);
      throw new Error("We could not save your alert. Please try again.");
    }
    return { ok: true as const };
  });

/** Lightweight live-status poll used by the browser-notification watcher. */
export const liveStatus = createServerFn({ method: "GET" }).handler(async () => {
  const { data } = await publicClient()
    .from("podcast_schedule")
    .select("id, title, status, starts_at, stream_url")
    .eq("published", true)
    .eq("status", "live")
    .order("starts_at", { ascending: true })
    .limit(1);
  const row = data?.[0];
  return row ? { live: true as const, ...row } : { live: false as const };
});

/* ---------------- admin ---------------- */

export const adminPodcastData = createServerFn({ method: "GET" })
  .middleware([requireSupabaseAuth])
  .handler(async ({ context }) => {
    await assertAdmin(context);
    const [schedule, episodes, guests, questions, subs] = await Promise.all([
      context.supabase.from("podcast_schedule").select("*").order("starts_at"),
      context.supabase.from("podcast_episodes").select("*").order("published_at", { ascending: false }),
      context.supabase.from("podcast_guests").select("*").order("created_at", { ascending: false }),
      context.supabase.from("podcast_questions").select("*").order("created_at", { ascending: false }).limit(200),
      context.supabase.from("podcast_subscribers").select("id"),
    ]);
    return {
      schedule: (schedule.data ?? []) as ScheduleRow[],
      episodes: (episodes.data ?? []) as EpisodeRow[],
      guests: (guests.data ?? []) as GuestRow[],
      questions: (questions.data ?? []) as QuestionRow[],
      subscriberCount: (subs.data ?? []).length,
    };
  });

const scheduleSchema = z.object({
  id: z.string().uuid().optional(),
  title: z.string().trim().min(3).max(160),
  topic: z.string().trim().max(400).default(""),
  starts_at: z.string().min(4),
  duration_minutes: z.coerce.number().int().min(5).max(600).default(60),
  status: z.enum(["scheduled", "live", "ended"]).default("scheduled"),
  stream_url: z.string().trim().max(500).optional().or(z.literal("")),
  published: z.boolean().default(true),
});

export const saveSchedule = createServerFn({ method: "POST" })
  .middleware([requireSupabaseAuth])
  .inputValidator((input: unknown) => scheduleSchema.parse(input))
  .handler(async ({ data, context }) => {
    await assertAdmin(context);
    const { id, ...values } = data;
    const payload = {
      ...values,
      stream_url: values.stream_url || null,
      starts_at: new Date(values.starts_at).toISOString(),
      updated_at: new Date().toISOString(),
    };
    const q = id
      ? context.supabase.from("podcast_schedule").update(payload).eq("id", id)
      : context.supabase.from("podcast_schedule").insert(payload);
    const { error } = await q;
    if (error) throw new Error(error.message);
    return { ok: true as const };
  });

const episodeSchema = z.object({
  id: z.string().uuid().optional(),
  slug: z.string().trim().min(3).max(120).regex(/^[a-z0-9-]+$/, "Use lowercase letters, numbers and dashes"),
  title: z.string().trim().min(3).max(200),
  summary: z.string().trim().max(600).default(""),
  show_notes: z.string().trim().max(8000).default(""),
  video_url: z.string().trim().max(500).optional().or(z.literal("")),
  cover_image_url: z.string().trim().max(500).optional().or(z.literal("")),
  duration_minutes: z.coerce.number().int().min(1).max(600).default(45),
  published: z.boolean().default(true),
});

export const saveEpisode = createServerFn({ method: "POST" })
  .middleware([requireSupabaseAuth])
  .inputValidator((input: unknown) => episodeSchema.parse(input))
  .handler(async ({ data, context }) => {
    await assertAdmin(context);
    const { id, ...values } = data;
    const payload = {
      ...values,
      video_url: values.video_url || null,
      cover_image_url: values.cover_image_url || null,
      updated_at: new Date().toISOString(),
    };
    const q = id
      ? context.supabase.from("podcast_episodes").update(payload).eq("id", id)
      : context.supabase.from("podcast_episodes").insert(payload);
    const { error } = await q;
    if (error) throw new Error(error.message);
    return { ok: true as const };
  });

const guestSchema = z.object({
  id: z.string().uuid().optional(),
  name: z.string().trim().min(2).max(120),
  role_title: z.string().trim().max(160).default(""),
  bio: z.string().trim().max(2000).default(""),
  avatar_url: z.string().trim().max(500).optional().or(z.literal("")),
  link_url: z.string().trim().max(500).optional().or(z.literal("")),
  episode_id: z.string().uuid().optional().or(z.literal("")),
  published: z.boolean().default(true),
});

export const saveGuest = createServerFn({ method: "POST" })
  .middleware([requireSupabaseAuth])
  .inputValidator((input: unknown) => guestSchema.parse(input))
  .handler(async ({ data, context }) => {
    await assertAdmin(context);
    const { id, ...values } = data;
    const payload = {
      ...values,
      avatar_url: values.avatar_url || null,
      link_url: values.link_url || null,
      episode_id: values.episode_id || null,
    };
    const q = id
      ? context.supabase.from("podcast_guests").update(payload).eq("id", id)
      : context.supabase.from("podcast_guests").insert(payload);
    const { error } = await q;
    if (error) throw new Error(error.message);
    return { ok: true as const };
  });

export const deletePodcastRow = createServerFn({ method: "POST" })
  .middleware([requireSupabaseAuth])
  .inputValidator((input: unknown) =>
    z
      .object({
        table: z.enum(["podcast_schedule", "podcast_episodes", "podcast_guests", "podcast_questions"]),
        id: z.string().uuid(),
      })
      .parse(input),
  )
  .handler(async ({ data, context }) => {
    await assertAdmin(context);
    const { error } = await context.supabase.from(data.table).delete().eq("id", data.id);
    if (error) throw new Error(error.message);
    return { ok: true as const };
  });

export const moderateQuestion = createServerFn({ method: "POST" })
  .middleware([requireSupabaseAuth])
  .inputValidator((input: unknown) =>
    z
      .object({
        id: z.string().uuid(),
        status: z.enum(["pending", "approved", "answered", "rejected"]),
        answer: z.string().trim().max(2000).optional(),
      })
      .parse(input),
  )
  .handler(async ({ data, context }) => {
    await assertAdmin(context);
    const { error } = await context.supabase
      .from("podcast_questions")
      .update({ status: data.status, answer: data.answer ?? null })
      .eq("id", data.id);
    if (error) throw new Error(error.message);
    return { ok: true as const };
  });

/** Marks a scheduled show live and emails every alert subscriber. */
export const announceGoLive = createServerFn({ method: "POST" })
  .middleware([requireSupabaseAuth])
  .inputValidator((input: unknown) => z.object({ id: z.string().uuid() }).parse(input))
  .handler(async ({ data, context }) => {
    await assertAdmin(context);
    const { data: show, error } = await context.supabase
      .from("podcast_schedule")
      .update({ status: "live", updated_at: new Date().toISOString() })
      .eq("id", data.id)
      .select("title")
      .maybeSingle();
    if (error) throw new Error(error.message);

    const { supabaseAdmin } = await import("@/integrations/supabase/client.server");
    const { data: subs } = await supabaseAdmin.from("podcast_subscribers").select("email");
    const { sendGoLiveEmails } = await import("./podcast-notify.server");
    const result = await sendGoLiveEmails(
      (subs ?? []).map((s: { email: string }) => s.email),
      show?.title ?? "Gatavase Live Podcast",
      "https://gatavase.lovable.app/live-podcast",
    );
    return { ok: true as const, ...result };
  });
