import { Channel, type Track } from "../channel";
import {
  saveChannel,
  deleteChannelFromDb,
  saveChannelQueue,
  updateChannelState,
  updateChannelName,
} from "../db";
import { state } from "../state";
import { broadcastChannelList } from "../broadcast";
import { generateChannelId, buildTracksFromIds } from "../init";
import { getOrCreateUser, userHasPermission } from "./helpers";

// Helper for persistence callback
function getPersistCallback() {
  return (channel: Channel, type: "state" | "queue") => {
    if (type === "state") {
      updateChannelState(channel.id, {
        currentIndex: channel.currentIndex,
        startedAt: channel.startedAt,
        paused: channel.paused,
        pausedAt: channel.pausedAt,
        playbackMode: channel.playbackMode,
      });
    } else if (type === "queue") {
      saveChannelQueue(channel.id, channel.queue.map(t => t.id));
    }
  };
}

// GET /api/channels - list channels
export function handleListChannels(req: Request, server: any): Response {
  const { user, headers } = getOrCreateUser(req, server);
  if (!user) {
    return Response.json({ error: "Authentication required" }, { status: 401 });
  }
  const list = [...state.channels.values()].map(c => c.getListInfo());
  return Response.json(list, { headers });
}

// POST /api/channels - create channel
export async function handleCreateChannel(req: Request, server: any): Promise<Response> {
  const { user } = getOrCreateUser(req, server);
  if (!user) {
    return Response.json({ error: "Authentication required" }, { status: 401 });
  }
  if (user.is_guest) {
    return Response.json({ error: "Guests cannot create channels" }, { status: 403 });
  }
  try {
    const { name, description, trackIds } = await req.json();
    if (!name || typeof name !== "string" || name.trim().length === 0) {
      return Response.json({ error: "Name is required" }, { status: 400 });
    }
    if (name.trim().length > 64) {
      return Response.json({ error: "Name must be 64 characters or less" }, { status: 400 });
    }

    let tracks: Track[];
    if (trackIds && Array.isArray(trackIds) && trackIds.length > 0) {
      tracks = buildTracksFromIds(trackIds, state.library);
    } else {
      tracks = [];
    }

    const channelId = generateChannelId();
    const channel = new Channel({
      id: channelId,
      name: name.trim(),
      description: description || "",
      tracks,
      createdBy: user.id,
      isDefault: false,
    });
    channel.setPersistenceCallback(getPersistCallback());
    state.channels.set(channelId, channel);

    saveChannel({
      id: channel.id,
      name: channel.name,
      description: channel.description,
      createdBy: channel.createdBy,
      isDefault: false,
      currentIndex: channel.currentIndex,
      startedAt: channel.startedAt,
      paused: channel.paused,
      pausedAt: channel.pausedAt,
      playbackMode: channel.playbackMode,
    });
    saveChannelQueue(channel.id, tracks.map(t => t.id));

    console.log(`[Channel] Created "${name.trim()}" (id=${channelId}) by user ${user.id}`);
    broadcastChannelList();
    return Response.json(channel.getListInfo(), { status: 201 });
  } catch {
    return Response.json({ error: "Invalid request" }, { status: 400 });
  }
}

// DELETE /api/channels/:id - delete channel
export function handleDeleteChannel(req: Request, server: any, channelId: string): Response {
  const { user } = getOrCreateUser(req, server);
  if (!user) {
    return Response.json({ error: "Authentication required" }, { status: 401 });
  }
  const channel = state.channels.get(channelId);
  if (!channel) {
    return Response.json({ error: "Channel not found" }, { status: 404 });
  }
  if (channel.isDefault) {
    return Response.json({ error: "Cannot delete default channel" }, { status: 403 });
  }
  if (!user.is_admin && channel.createdBy !== user.id) {
    return Response.json({ error: "Access denied" }, { status: 403 });
  }

  // Move connected clients to default channel before deleting
  const defaultChannel = [...state.channels.values()].find(c => c.isDefault);
  if (defaultChannel && channel.clients.size > 0) {
    for (const ws of channel.clients) {
      channel.removeClient(ws);
      ws.data.channelId = defaultChannel.id;
      defaultChannel.addClient(ws);
      ws.send(JSON.stringify({ type: "switched", channelId: defaultChannel.id }));
    }
  }

  state.channels.delete(channelId);
  deleteChannelFromDb(channelId);
  broadcastChannelList();
  return Response.json({ success: true });
}

// PATCH /api/channels/:id - rename channel
export async function handleRenameChannel(req: Request, server: any, channelId: string): Promise<Response> {
  const { user } = getOrCreateUser(req, server);
  if (!user) {
    return Response.json({ error: "Authentication required" }, { status: 401 });
  }
  if (user.is_guest) {
    return Response.json({ error: "Guests cannot rename channels" }, { status: 403 });
  }
  const channel = state.channels.get(channelId);
  if (!channel) {
    return Response.json({ error: "Channel not found" }, { status: 404 });
  }
  try {
    const { name } = await req.json();
    if (!name || typeof name !== "string" || name.trim().length === 0) {
      return Response.json({ error: "Name is required" }, { status: 400 });
    }
    if (name.trim().length > 64) {
      return Response.json({ error: "Name must be 64 characters or less" }, { status: 400 });
    }
    const newName = name.trim();
    channel.name = newName;
    updateChannelName(channelId, newName);
    console.log(`[Channel] Renamed "${channelId}" to "${newName}" by user ${user.id}`);
    broadcastChannelList();
    return Response.json({ success: true, name: newName });
  } catch {
    return Response.json({ error: "Invalid request" }, { status: 400 });
  }
}

// GET /api/channels/:id - get channel state
export function handleGetChannel(channelId: string): Response {
  const channel = state.channels.get(channelId);
  if (!channel) return new Response("Not found", { status: 404 });
  return Response.json(channel.getState());
}

// POST /api/channels/:id/jump - jump to track
export async function handleJump(req: Request, server: any, channelId: string): Promise<Response> {
  const { user } = getOrCreateUser(req, server);
  if (!userHasPermission(user, "channel", channelId, "control")) {
    return new Response("Forbidden", { status: 403 });
  }
  const channel = state.channels.get(channelId);
  if (!channel) return new Response("Not found", { status: 404 });
  try {
    const body = await req.json();
    if (typeof body.index === "number") {
      channel.jumpTo(body.index);
      return Response.json({ success: true });
    }
    return new Response("Invalid index", { status: 400 });
  } catch {
    return new Response("Invalid JSON", { status: 400 });
  }
}

// POST /api/channels/:id/seek - seek in channel
export async function handleSeek(req: Request, server: any, channelId: string): Promise<Response> {
  const { user } = getOrCreateUser(req, server);
  if (!userHasPermission(user, "channel", channelId, "control")) {
    return new Response("Forbidden", { status: 403 });
  }
  const channel = state.channels.get(channelId);
  if (!channel) return new Response("Not found", { status: 404 });
  try {
    const body = await req.json();
    if (typeof body.timestamp === "number") {
      channel.seek(body.timestamp);
      return Response.json({ success: true });
    }
    return new Response("Invalid timestamp", { status: 400 });
  } catch {
    return new Response("Invalid JSON", { status: 400 });
  }
}

// PATCH /api/channels/:id/queue - modify queue
export async function handleModifyQueue(req: Request, server: any, channelId: string): Promise<Response> {
  const { user } = getOrCreateUser(req, server);
  if (!userHasPermission(user, "channel", channelId, "control")) {
    return new Response("Forbidden", { status: 403 });
  }
  const channel = state.channels.get(channelId);
  if (!channel) return new Response("Not found", { status: 404 });

  try {
    const body = await req.json();
    const { add, remove, set, insertAt, move, to } = body;

    if (Array.isArray(set)) {
      const tracks = buildTracksFromIds(set, state.library);
      channel.setQueue(tracks);
      return Response.json({ success: true, queueLength: channel.queue.length });
    }

    // Move/reorder tracks within queue
    if (Array.isArray(move) && typeof to === "number") {
      channel.moveTracks(move, to);
      return Response.json({ success: true, queueLength: channel.queue.length });
    }

    if (Array.isArray(remove) && remove.length > 0) {
      const indices = remove.filter((i: unknown) => typeof i === "number");
      channel.removeTracksByIndex(indices);
    }

    if (Array.isArray(add) && add.length > 0) {
      const tracks = buildTracksFromIds(add, state.library);
      if (typeof insertAt === "number") {
        channel.insertTracksAt(tracks, insertAt);
      } else {
        channel.addTracks(tracks);
      }
    }

    return Response.json({ success: true, queueLength: channel.queue.length });
  } catch {
    return new Response("Invalid JSON", { status: 400 });
  }
}

// POST /api/channels/:id/mode - set playback mode
export async function handleSetMode(req: Request, server: any, channelId: string): Promise<Response> {
  const { user } = getOrCreateUser(req, server);
  if (!userHasPermission(user, "channel", channelId, "control")) {
    return new Response("Forbidden", { status: 403 });
  }
  const channel = state.channels.get(channelId);
  if (!channel) return new Response("Not found", { status: 404 });
  try {
    const body = await req.json();
    const validModes = ["once", "repeat-all", "repeat-one", "shuffle"];
    if (typeof body.mode === "string" && validModes.includes(body.mode)) {
      channel.setPlaybackMode(body.mode);
      return Response.json({ success: true, playbackMode: channel.playbackMode });
    }
    return new Response("Invalid mode", { status: 400 });
  } catch {
    return new Response("Invalid JSON", { status: 400 });
  }
}
