import { getServiceSupabase } from "@/lib/supabase/client";

export interface TaskAttachment {
  id: string;
  name: string;
  type: string;
  size: number;
  dataUrl: string;
  uploadedAt: string;
}

export interface TaskCommentAuthor {
  id: string;
  name: string;
  email?: string;
  avatarUrl?: string;
  type: "human" | "assistant";
}

export interface TaskComment {
  id: string;
  text: string;
  createdAt: string;
  author: TaskCommentAuthor | "user" | "assistant";
  replies?: TaskComment[];
}

export interface Task {
  id: string;
  title: string;
  description?: string;
  type: "idea" | "task" | "bug" | "research" | "plan";
  status: "open" | "todo" | "blocked" | "in-progress" | "review" | "validate" | "archived" | "canceled" | "done";
  priority: "low" | "medium" | "high" | "urgent";
  projectId: string;
  sprintId?: string;
  createdAt: string;
  updatedAt: string;
  createdById?: string;
  createdByName?: string;
  createdByAvatarUrl?: string;
  updatedById?: string;
  updatedByName?: string;
  updatedByAvatarUrl?: string;
  assigneeId?: string;
  assigneeName?: string;
  assigneeEmail?: string;
  assigneeAvatarUrl?: string;
  dueDate?: string;
  comments: TaskComment[];
  tags: string[];
  attachments: TaskAttachment[];
}

export interface Project {
  id: string;
  name: string;
  description?: string;
  color: string;
  createdAt: string;
}

export interface Sprint {
  id: string;
  name: string;
  goal?: string;
  startDate: string;
  endDate: string;
  status: "planning" | "active" | "completed";
  projectId: string;
  createdAt: string;
}

export interface DataStore {
  projects: Project[];
  tasks: Task[];
  sprints: Sprint[];
  lastUpdated: number;
}

const defaultData: DataStore = {
  projects: [
    { id: "1", name: "OpenClaw iOS", description: "Main iOS app development", color: "#8b5cf6", createdAt: new Date().toISOString() },
    { id: "2", name: "Web Projects", description: "Web tools and dashboards", color: "#3b82f6", createdAt: new Date().toISOString() },
    { id: "3", name: "Research", description: "Experiments and learning", color: "#10b981", createdAt: new Date().toISOString() },
  ],
  tasks: [],
  sprints: [],
  lastUpdated: Date.now(),
};

// Helper to safely parse JSON arrays
function safeParseArray<T>(value: unknown, fallback: T[]): T[] {
  if (!value) return fallback;
  if (Array.isArray(value)) return value as T[];
  try {
    const parsed = JSON.parse(String(value));
    return Array.isArray(parsed) ? (parsed as T[]) : fallback;
  } catch {
    return fallback;
  }
}

// Normalize attachments
function normalizeAttachments(attachments: unknown): TaskAttachment[] {
  if (!Array.isArray(attachments)) return [];

  return attachments
    .map((attachment: unknown) => {
      if (!attachment || typeof attachment !== "object") return null;
      const value = attachment as Partial<TaskAttachment>;
      const name = typeof value.name === "string" ? value.name.trim() : "";
      const dataUrl = typeof value.dataUrl === "string" ? value.dataUrl : "";
      if (!name || !dataUrl) return null;

      return {
        id: typeof value.id === "string" && value.id ? value.id : `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
        name,
        type: typeof value.type === "string" ? value.type : "application/octet-stream",
        size: typeof value.size === "number" && Number.isFinite(value.size) ? value.size : 0,
        dataUrl,
        uploadedAt: typeof value.uploadedAt === "string" && value.uploadedAt ? value.uploadedAt : new Date().toISOString(),
      };
    })
    .filter((attachment): attachment is TaskAttachment => attachment !== null);
}

// Normalize comment author
function normalizeCommentAuthor(author: unknown): TaskCommentAuthor {
  if (author === "assistant") {
    return { id: "assistant", name: "Assistant", type: "assistant" };
  }
  if (author === "user") {
    return { id: "legacy-user", name: "User", type: "human" };
  }

  if (!author || typeof author !== "object") {
    return { id: "legacy-user", name: "User", type: "human" };
  }

  const value = author as Partial<TaskCommentAuthor>;
  const type: TaskCommentAuthor["type"] =
    value.type === "assistant" || value.id === "assistant" ? "assistant" : "human";
  const id = typeof value.id === "string" && value.id.trim().length > 0
    ? value.id
    : type === "assistant" ? "assistant" : "legacy-user";
  const name = typeof value.name === "string" && value.name.trim().length > 0
    ? value.name.trim()
    : type === "assistant" ? "Assistant" : "User";
  const email = typeof value.email === "string" && value.email.trim().length > 0 ? value.email.trim() : undefined;
  const avatarUrl = typeof value.avatarUrl === "string" && value.avatarUrl.trim().length > 0 ? value.avatarUrl : undefined;

  return { id, name, email, avatarUrl, type };
}

// Normalize comments
function normalizeComments(comments: unknown): TaskComment[] {
  if (!Array.isArray(comments)) return [];

  const normalized: TaskComment[] = [];
  for (const entry of comments) {
    if (!entry || typeof entry !== "object") continue;
    const value = entry as Partial<TaskComment>;
    if (typeof value.id !== "string" || typeof value.text !== "string") continue;

    normalized.push({
      id: value.id,
      text: value.text,
      createdAt: typeof value.createdAt === "string" ? value.createdAt : new Date().toISOString(),
      author: normalizeCommentAuthor(value.author),
      replies: normalizeComments(value.replies),
    });
  }

  return normalized;
}

// Normalize a task from database row
function normalizeTask(task: Record<string, unknown>): Task {
  const comments = safeParseArray<unknown>(task.comments, []);
  const tags = safeParseArray<unknown>(task.tags, []);
  const attachments = safeParseArray<unknown>(task.attachments, []);

  return {
    id: String(task.id ?? ""),
    title: String(task.title ?? ""),
    description: task.description ? String(task.description) : undefined,
    type: (task.type as Task["type"]) ?? "task",
    status: (task.status as Task["status"]) ?? "open",
    priority: (task.priority as Task["priority"]) ?? "medium",
    projectId: String(task.project_id ?? ""),
    sprintId: task.sprint_id ? String(task.sprint_id) : undefined,
    createdAt: String(task.created_at ?? new Date().toISOString()),
    updatedAt: String(task.updated_at ?? new Date().toISOString()),
    createdById: task.created_by_id ? String(task.created_by_id) : undefined,
    createdByName: task.created_by_name ? String(task.created_by_name) : undefined,
    createdByAvatarUrl: task.created_by_avatar_url ? String(task.created_by_avatar_url) : undefined,
    updatedById: task.updated_by_id ? String(task.updated_by_id) : undefined,
    updatedByName: task.updated_by_name ? String(task.updated_by_name) : undefined,
    updatedByAvatarUrl: task.updated_by_avatar_url ? String(task.updated_by_avatar_url) : undefined,
    assigneeId: task.assignee_id ? String(task.assignee_id) : undefined,
    assigneeName: task.assignee_name ? String(task.assignee_name) : undefined,
    assigneeEmail: task.assignee_email ? String(task.assignee_email) : undefined,
    assigneeAvatarUrl: task.assignee_avatar_url ? String(task.assignee_avatar_url) : undefined,
    dueDate: task.due_date ? String(task.due_date) : undefined,
    comments: normalizeComments(comments),
    tags: tags.filter((tag): tag is string => typeof tag === "string"),
    attachments: normalizeAttachments(attachments),
  };
}

// Fetch user lookup map
async function getUserLookup(): Promise<Map<string, { id: string; name: string; email?: string; avatarUrl?: string }>> {
  const supabase = getServiceSupabase();
  const { data: users } = await supabase
    .from("users")
    .select("id, name, email, avatar_url");

  const lookup = new Map<string, { id: string; name: string; email?: string; avatarUrl?: string }>();
  for (const user of users || []) {
    lookup.set(user.id, {
      id: user.id,
      name: user.name,
      email: user.email ?? undefined,
      avatarUrl: user.avatar_url ?? undefined,
    });
  }
  return lookup;
}

export async function getData(): Promise<DataStore> {
  const supabase = getServiceSupabase();
  const usersById = await getUserLookup();

  // Fetch all data in parallel
  const [{ data: projects }, { data: sprints }, { data: tasks }, { data: meta }] = await Promise.all([
    supabase.from("projects").select("*").order("created_at", { ascending: true }),
    supabase.from("sprints").select("*").order("start_date", { ascending: true }),
    supabase.from("tasks").select("*").order("created_at", { ascending: true }),
    supabase.from("meta").select("value").eq("key", "lastUpdated").maybeSingle(),
  ]);

  // If no data exists, seed with defaults
  if ((projects?.length ?? 0) === 0 && (tasks?.length ?? 0) === 0 && (sprints?.length ?? 0) === 0) {
    await seedDefaultData();
    return getData();
  }

  return {
    projects: (projects || []).map((p) => ({
      id: p.id,
      name: p.name,
      description: p.description ?? undefined,
      color: p.color,
      createdAt: p.created_at,
    })),
    sprints: (sprints || []).map((s) => ({
      id: s.id,
      name: s.name,
      goal: s.goal ?? undefined,
      startDate: s.start_date,
      endDate: s.end_date,
      status: s.status,
      projectId: s.project_id,
      createdAt: s.created_at,
    })),
    tasks: (tasks || []).map((t) => {
      const createdByUser = t.created_by_id ? usersById.get(t.created_by_id) : undefined;
      const updatedByUser = t.updated_by_id ? usersById.get(t.updated_by_id) : undefined;
      const assigneeUser = t.assignee_id ? usersById.get(t.assignee_id) : undefined;

      return {
        id: t.id,
        title: t.title,
        description: t.description ?? undefined,
        type: t.type,
        status: t.status,
        priority: t.priority,
        projectId: t.project_id,
        sprintId: t.sprint_id ?? undefined,
        createdAt: t.created_at,
        updatedAt: t.updated_at,
        createdById: t.created_by_id ?? undefined,
        createdByName: t.created_by_name ?? createdByUser?.name ?? undefined,
        createdByAvatarUrl: createdByUser?.avatarUrl ?? t.created_by_avatar_url ?? undefined,
        updatedById: t.updated_by_id ?? undefined,
        updatedByName: t.updated_by_name ?? updatedByUser?.name ?? undefined,
        updatedByAvatarUrl: updatedByUser?.avatarUrl ?? t.updated_by_avatar_url ?? undefined,
        assigneeId: t.assignee_id ?? undefined,
        assigneeName: assigneeUser?.name ?? t.assignee_name ?? undefined,
        assigneeEmail: assigneeUser?.email ?? t.assignee_email ?? undefined,
        assigneeAvatarUrl: assigneeUser?.avatarUrl ?? undefined,
        dueDate: t.due_date ?? undefined,
        comments: normalizeComments(t.comments),
        tags: safeParseArray<unknown>(t.tags, []).filter((tag): tag is string => typeof tag === "string"),
        attachments: normalizeAttachments(t.attachments),
      };
    }),
    lastUpdated: Number(meta?.value ?? Date.now()),
  };
}

async function seedDefaultData(): Promise<void> {
  const supabase = getServiceSupabase();
  const now = new Date().toISOString();

  // Insert default projects
  for (const project of defaultData.projects) {
    await supabase.from("projects").insert({
      id: project.id,
      name: project.name,
      description: project.description,
      color: project.color,
      created_at: project.createdAt,
    });
  }

  // Update lastUpdated
  await supabase.from("meta").upsert({
    key: "lastUpdated",
    value: String(Date.now()),
    updated_at: now,
  });
}

export async function saveData(data: DataStore): Promise<DataStore> {
  const supabase = getServiceSupabase();
  const now = new Date().toISOString();
  const lastUpdated = Date.now();

  // Delete existing data (in correct order due to FK constraints)
  const { error: deleteTasksError } = await supabase.from("tasks").delete().neq("id", "");
  const { error: deleteSprintsError } = await supabase.from("sprints").delete().neq("id", "");
  const { error: deleteProjectsError } = await supabase.from("projects").delete().neq("id", "");
  
  if (deleteTasksError) console.error("Failed to delete tasks:", deleteTasksError);
  if (deleteSprintsError) console.error("Failed to delete sprints:", deleteSprintsError);
  if (deleteProjectsError) console.error("Failed to delete projects:", deleteProjectsError);

  // Insert projects
  if (data.projects.length > 0) {
    const { error: projectError } = await supabase.from("projects").insert(
      data.projects.map((p) => ({
        id: p.id,
        name: p.name,
        description: p.description,
        color: p.color,
        created_at: p.createdAt,
      }))
    );
    if (projectError) {
      console.error("Failed to insert projects:", projectError);
      throw new Error(`Failed to insert projects: ${projectError.message}`);
    }
  }

  // Insert sprints
  if (data.sprints.length > 0) {
    const { error: sprintError } = await supabase.from("sprints").insert(
      data.sprints.map((s) => ({
        id: s.id,
        name: s.name,
        goal: s.goal,
        start_date: s.startDate,
        end_date: s.endDate,
        status: s.status,
        project_id: s.projectId,
        created_at: s.createdAt,
      }))
    );
    if (sprintError) {
      console.error("Failed to insert sprints:", sprintError);
      throw new Error(`Failed to insert sprints: ${sprintError.message}`);
    }
  }

  // Insert tasks
  if (data.tasks.length > 0) {
    const { error: taskError } = await supabase.from("tasks").insert(
      data.tasks.map((t) => ({
        id: t.id,
        title: t.title,
        description: t.description,
        type: t.type,
        status: t.status,
        priority: t.priority,
        project_id: t.projectId,
        sprint_id: t.sprintId,
        created_at: t.createdAt,
        updated_at: now,
        created_by_id: t.createdById,
        updated_by_id: t.updatedById,
        assignee_id: t.assigneeId,
        due_date: t.dueDate,
        comments: t.comments,
        tags: t.tags,
        attachments: t.attachments,
      }))
    );
    if (taskError) {
      console.error("Failed to insert tasks:", taskError);
      throw new Error(`Failed to insert tasks: ${taskError.message}`);
    }
  }

  // Update lastUpdated
  const { error: metaError } = await supabase.from("meta").upsert({
    key: "lastUpdated",
    value: String(lastUpdated),
    updated_at: now,
  });

  return getData();
}
