import { createClient } from "@/lib/supabase/client";
import type { CanvasData, DraggableElement } from "./types";
import type { CanvasDataRow } from "@/lib/supabase/types";

/**
 * Supabase database operations for canvas data
 * Replaces the previous IndexedDB/Dexie implementation
 */

// Convert database row to CanvasData format
function rowToCanvasData(row: {
  id: string;
  team_id: string;
  canvas_id: string;
  fields: unknown;
  elements: unknown;
  metadata: unknown;
  created_at: string;
  updated_at: string;
}, templateData: { week: 2 | 3; day: number; title: string }): CanvasData {
  return {
    id: row.canvas_id,
    templateId: row.canvas_id,
    week: templateData.week,
    day: templateData.day as 1 | 2 | 3 | 4 | 5,
    title: templateData.title,
    fields: (row.fields as Record<string, string | string[] | null>) || {},
    elements: (row.elements as DraggableElement[]) || [],
    metadata: (row.metadata as CanvasData["metadata"]) || {},
    createdAt: new Date(row.created_at).getTime(),
    updatedAt: new Date(row.updated_at).getTime(),
  };
}

/**
 * Canvas CRUD operations with Supabase
 */
export const canvasDB = {
  /**
   * Get all canvases for the current user's team
   */
  async getAll(teamId: string): Promise<{ canvas_id: string; fields: unknown; elements: unknown; metadata: unknown }[]> {
    const supabase = createClient();

    const { data, error } = await supabase
      .from("canvas_data")
      .select("*")
      .eq("team_id", teamId);

    if (error) {
      console.error("Error fetching canvases:", error);
      return [];
    }

    return data || [];
  },

  /**
   * Get canvas by ID for a specific team
   */
  async get(teamId: string, canvasId: string): Promise<{
    fields: Record<string, string | string[] | null>;
    elements: DraggableElement[];
    metadata: CanvasData["metadata"];
  } | null> {
    const supabase = createClient();

    const { data, error } = await supabase
      .from("canvas_data")
      .select("*")
      .eq("team_id", teamId)
      .eq("canvas_id", canvasId)
      .maybeSingle();

    if (error) {
      console.error(`Error fetching canvas (team: ${teamId}, canvas: ${canvasId}):`, error);
      return null;
    }

    if (!data) {
      return null;
    }

    // Cast to proper row type to work around Supabase type inference
    const row = data as unknown as CanvasDataRow;

    return {
      fields: (row.fields as Record<string, string | string[] | null>) || {},
      elements: (row.elements as unknown as DraggableElement[]) || [],
      metadata: (row.metadata as CanvasData["metadata"]) || {},
    };
  },

  /**
   * Save or update canvas data (upsert)
   * 
   * IMPORTANT: This method merges fields with existing database data to support
   * concurrent editing. When multiple users edit the same canvas simultaneously,
   * their changes to different fields are preserved instead of overwriting each other.
   */
  async save(teamId: string, canvasId: string, data: {
    fields: Record<string, string | string[] | null>;
    elements?: DraggableElement[];
    metadata?: CanvasData["metadata"];
  }): Promise<boolean> {
    const supabase = createClient();

    // Fetch existing data to merge with - this prevents concurrent edits from
    // overwriting each other by only updating fields that were actually changed
    const existing = await this.get(teamId, canvasId);

    // Merge fields: existing fields + new fields (new fields take precedence)
    // This ensures that if User A edits field "cell-1" and User B edits "cell-2",
    // both changes are preserved even if their saves overlap
    const mergedFields = {
      ...(existing?.fields || {}),
      ...data.fields,
    };

    // Merge elements: if new elements provided, use them; otherwise keep existing
    // For elements, we do full replacement since element order/positions matter
    const mergedElements = data.elements !== undefined
      ? data.elements
      : (existing?.elements || []);

    // Merge metadata: combine existing with new (new takes precedence)
    const mergedMetadata = {
      ...(existing?.metadata || {}),
      ...(data.metadata || {}),
    };

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const { error } = await (supabase
      .from("canvas_data") as any)
      .upsert({
        team_id: teamId,
        canvas_id: canvasId,
        fields: mergedFields,
        elements: mergedElements,
        metadata: mergedMetadata,
        updated_at: new Date().toISOString(),
      }, {
        onConflict: "team_id,canvas_id",
      });

    if (error) {
      console.error("Error saving canvas:", error);
      return false;
    }

    return true;
  },

  /**
   * Update specific fields in a canvas
   */
  async updateFields(
    teamId: string,
    canvasId: string,
    fields: Record<string, string | string[] | null>
  ): Promise<boolean> {
    const supabase = createClient();

    // First, get existing data
    const existing = await this.get(teamId, canvasId);

    const mergedFields = {
      ...(existing?.fields || {}),
      ...fields,
    };

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const { error } = await (supabase
      .from("canvas_data") as any)
      .upsert({
        team_id: teamId,
        canvas_id: canvasId,
        fields: mergedFields,
        elements: existing?.elements || [],
        metadata: existing?.metadata || {},
        updated_at: new Date().toISOString(),
      }, {
        onConflict: "team_id,canvas_id",
      });

    if (error) {
      console.error("Error updating fields:", error);
      return false;
    }

    return true;
  },

  /**
   * Update draggable elements in a canvas
   */
  async updateElements(
    teamId: string,
    canvasId: string,
    elements: DraggableElement[]
  ): Promise<boolean> {
    const supabase = createClient();

    // First, get existing data
    const existing = await this.get(teamId, canvasId);

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const { error } = await (supabase
      .from("canvas_data") as any)
      .upsert({
        team_id: teamId,
        canvas_id: canvasId,
        fields: existing?.fields || {},
        elements: elements,
        metadata: existing?.metadata || {},
        updated_at: new Date().toISOString(),
      }, {
        onConflict: "team_id,canvas_id",
      });

    if (error) {
      console.error("Error updating elements:", error);
      return false;
    }

    return true;
  },

  /**
   * Delete canvas data
   */
  async delete(teamId: string, canvasId: string): Promise<boolean> {
    const supabase = createClient();

    const { error } = await supabase
      .from("canvas_data")
      .delete()
      .eq("team_id", teamId)
      .eq("canvas_id", canvasId);

    if (error) {
      console.error("Error deleting canvas:", error);
      return false;
    }

    return true;
  },

  /**
   * Clear all canvas data for a team (use with caution!)
   */
  async clearAll(teamId: string): Promise<boolean> {
    const supabase = createClient();

    const { error } = await supabase
      .from("canvas_data")
      .delete()
      .eq("team_id", teamId);

    if (error) {
      console.error("Error clearing all canvases:", error);
      return false;
    }

    return true;
  },
};

/**
 * Load all canvas data for a team from DB
 * Returns a map of canvasId -> partial canvas data
 */
export async function loadTeamCanvasData(teamId: string): Promise<Record<string, {
  fields: Record<string, string | string[] | null>;
  elements: DraggableElement[];
  metadata: CanvasData["metadata"];
}>> {
  const rows = await canvasDB.getAll(teamId);

  const result: Record<string, {
    fields: Record<string, string | string[] | null>;
    elements: DraggableElement[];
    metadata: CanvasData["metadata"];
  }> = {};

  for (const row of rows) {
    result[row.canvas_id] = {
      fields: (row.fields as Record<string, string | string[] | null>) || {},
      elements: (row.elements as DraggableElement[]) || [],
      metadata: (row.metadata as CanvasData["metadata"]) || {},
    };
  }

  return result;
}

/**
 * Week lock operations with Supabase
 * Manages which weeks are locked/unlocked for teams
 */
export const weekLockDB = {
  /**
   * Get all week lock statuses
   */
  async getAll(): Promise<{ week: number; is_locked: boolean }[]> {
    const supabase = createClient();

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const { data, error } = await (supabase as any)
      .from("week_locks")
      .select("week, is_locked")
      .order("week");

    if (error) {
      console.error("Error fetching week locks:", error);
      return [];
    }

    return data || [];
  },

  /**
   * Set the lock status for a specific week
   */
  async setLocked(week: 1 | 2 | 3, isLocked: boolean): Promise<boolean> {
    const supabase = createClient();

    const { data: { user } } = await supabase.auth.getUser();

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const { error } = await (supabase as any)
      .from("week_locks")
      .update({
        is_locked: isLocked,
        locked_by: user?.id || null,
        updated_at: new Date().toISOString(),
      })
      .eq("week", week);

    if (error) {
      console.error("Error updating week lock:", error);
      return false;
    }

    return true;
  },

  /**
   * Check if a specific week is locked
   */
  async isLocked(week: 1 | 2 | 3): Promise<boolean> {
    const supabase = createClient();

    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const { data, error } = await (supabase as any)
      .from("week_locks")
      .select("is_locked")
      .eq("week", week)
      .single();

    if (error) {
      console.error("Error checking week lock:", error);
      return false;
    }

    return data?.is_locked ?? false;
  },
};
