/**
 * Compatibility shim that preserves the historical Supabase client
 * query-builder API surface (`.from().select()...`) for the legacy
 * public-storefront and admin pages that still call into it. The SaaS
 * rebuild no longer uses Supabase as a backend — server actions and
 * API routes connect directly to Postgres via `pg` / `withDb` from
 * `@/db/client`. This shim exists so the build keeps passing while
 * the remaining legacy call sites are migrated to Drizzle / raw `pg`
 * queries.
 *
 * IMPORTANT: This shim does NOT talk to a real database. It returns
 * empty result sets. Legacy call sites that need real data must be
 * rewritten against `pool` / `withDb` / `withBrand`.
 *
 * The query-builder API surface supported here is intentionally narrow:
 *   - .from(table).select(cols?).eq(col, val).eq(...).is(col, null).
 *     order(col, opts?).limit(n).range(min, max).single() → returns
 *     `{ data, error }` like the Supabase client did.
 *   - .from(table).insert(payload).select().single() for legacy inserts
 *   - .from(table).upsert(payload)
 *   - .from(table).update(payload).eq(col, val)
 *   - .from(table).delete().eq(col, val)
 *   - .rpc(fnName, params) — returns `{ data, error }` (data is null)
 *   - .auth.{getSession, getUser, signInWithPassword, signOut,
 *     updateUser, onAuthStateChange} — all return null / no-ops
 *   - .storage.from(bucket).{upload, download, remove, list}
 *   - .channel().on().subscribe()
 *
 * If a call site needs more than that, migrate the call site.
 */

type FilterOp = "eq" | "neq" | "gt" | "gte" | "lt" | "lte" | "is" | "like" | "ilike" | "in";
type Filter = { column: string; value: unknown; op: FilterOp };

// Result types for the mock query builder
interface MockQueryResult<T> {
  data: T[] | null;
  error: null;
}

interface MockSingleResult<T> {
  data: T | null;
  error: null;
}

class MockQueryBuilder<T extends Record<string, unknown> = Record<string, unknown>> {
  private tableName: string;
  private filters: Filter[] = [];
  private selectColumns: string = "*";
  private orderColumn?: string;
  private orderDirection?: "asc" | "desc";
  private limitValue?: number;
  private rangeMin?: number;
  private rangeMax?: number;
  private mode: "select" | "update" | "delete" = "select";
  private mutationData: Record<string, unknown> | null = null;

  constructor(tableName: string) {
    this.tableName = tableName;
    // No mock data - return empty results
  }

  select(columns: string = "*", _opts?: Record<string, unknown>) {
    this.selectColumns = columns;
    return this;
  }

  eq(column: string, value: unknown) {
    this.filters.push({ column, value, op: "eq" });
    return this;
  }

  neq(column: string, value: unknown) {
    this.filters.push({ column, value, op: "neq" });
    return this;
  }

  gt(column: string, value: unknown) {
    this.filters.push({ column, value, op: "gt" });
    return this;
  }

  gte(column: string, value: unknown) {
    this.filters.push({ column, value, op: "gte" });
    return this;
  }

  lt(column: string, value: unknown) {
    this.filters.push({ column, value, op: "lt" });
    return this;
  }

  lte(column: string, value: unknown) {
    this.filters.push({ column, value, op: "lte" });
    return this;
  }

  is(column: string, value: unknown) {
    this.filters.push({ column, value, op: "is" });
    return this;
  }

  like(column: string, value: string) {
    this.filters.push({ column, value, op: "like" });
    return this;
  }

  ilike(column: string, value: string) {
    this.filters.push({ column, value, op: "ilike" });
    return this;
  }

  in(column: string, values: unknown[]) {
    this.filters.push({ column, value: values, op: "in" });
    return this;
  }

  order(column: string, options?: { ascending?: boolean; nullsFirst?: boolean }) {
    this.orderColumn = column;
    this.orderDirection = options?.ascending === false ? "desc" : "asc";
    return this;
  }

  limit(count: number) {
    this.limitValue = count;
    return this;
  }

  range(min: number, max: number) {
    this.rangeMin = min;
    this.rangeMax = max;
    return this;
  }

  // The legacy Supabase client returns a thenable from .single() so
  // callers can write `.single().then(({ data, error }) => ...)` as
  // well as `const { data, error } = await ....single()`. We return a
  // proper Promise<MockSingleResult<T>> so destructured binding
  // patterns in callers work under `--strict` (no implicit `any`).
  single(): Promise<MockSingleResult<T>> {
    return Promise.resolve(this.executeSingle());
  }

  maybeSingle(): Promise<MockSingleResult<T>> {
    return Promise.resolve(this.executeSingle());
  }

  then<TResult1 = MockQueryResult<T>, TResult2 = never>(
    onfulfilled?: ((value: MockQueryResult<T>) => TResult1 | PromiseLike<TResult1>) | null,
    _onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,
  ): PromiseLike<TResult1 | TResult2> {
    const result = this.execute();
    return Promise.resolve(onfulfilled ? onfulfilled(result) : (result as unknown as TResult1));
  }

  // Insert / update / delete mutators — these are mostly used by legacy
  // auth flows and the AI preferences action. We capture the data and
  // short-circuit to a successful no-op response so the call sites
  // don't blow up. Real writes must go through server actions.
  executeSingle(): MockSingleResult<T> {
    const result = this.execute();
    if (Array.isArray(result.data) && result.data.length > 0) {
      return { data: result.data[0] as T, error: null };
    }
    return { data: null, error: null };
  }

  execute(): MockQueryResult<T> {
    if (this.mode === "update" || this.mode === "delete") {
      return this.runMutation();
    }
    // No mock data - return empty array
    let filtered: unknown[] = [];

    for (const filter of this.filters) {
      filtered = filtered.filter((row) => {
        const r = row as Record<string, unknown>;
        const rowValue = r[filter.column];
        switch (filter.op) {
          case "eq":
            return rowValue === filter.value;
          case "neq":
            return rowValue !== filter.value;
          case "gt":
            return (rowValue as number) > (filter.value as number);
          case "gte":
            return (rowValue as number) >= (filter.value as number);
          case "lt":
            return (rowValue as number) < (filter.value as number);
          case "lte":
            return (rowValue as number) <= (filter.value as number);
          case "is":
            if (filter.value === null) return rowValue === null;
            if (filter.value === undefined) return rowValue === undefined;
            return rowValue === filter.value;
          case "like":
            return (
              typeof rowValue === "string" &&
              rowValue.includes((filter.value as string).replace(/%/g, ""))
            );
          case "ilike":
            return (
              typeof rowValue === "string" &&
              rowValue
                .toLowerCase()
                .includes((filter.value as string).replace(/%/g, "").toLowerCase())
            );
          case "in":
            return Array.isArray(filter.value) && filter.value.includes(rowValue);
          default:
            return true;
        }
      });
    }

    if (this.orderColumn) {
      filtered.sort((a: unknown, b: unknown) => {
        const aVal = (a as Record<string, unknown>)[this.orderColumn!] as string | number;
        const bVal = (b as Record<string, unknown>)[this.orderColumn!] as string | number;
        if (aVal < bVal) return this.orderDirection === "desc" ? 1 : -1;
        if (aVal > bVal) return this.orderDirection === "desc" ? -1 : 1;
        return 0;
      });
    }

    if (this.rangeMin !== undefined && this.rangeMax !== undefined) {
      filtered = filtered.slice(this.rangeMin, this.rangeMax + 1);
    } else if (this.limitValue !== undefined) {
      filtered = filtered.slice(0, this.limitValue);
    }

    return { data: filtered as T[], error: null };
  }

  private runMutation(): MockQueryResult<T> {
    if (this.mode === "delete") {
      return { data: null, error: null };
    }
    if (this.mutationData) {
      const items = [this.mutationData];
      const returning = items.map((item, i) => ({
        ...item,
        id: (item as Record<string, unknown>).id ?? `generated-${Date.now()}-${i}`,
      }));
      return { data: returning as unknown as T[], error: null };
    }
    return { data: null, error: null };
  }
}

interface MockMutationResult<T> {
  data: T[] | null;
  error: null;
}

class MockMutationBuilder<T extends Record<string, unknown> = Record<string, unknown>> {
  private tableName: string;
  private data: unknown;

  constructor(tableName: string, data: unknown) {
    this.tableName = tableName;
    this.data = data;
  }

  select() {
    return new MockQueryBuilder<T>(this.tableName);
  }

  eq() {
    return this;
  }

  then<TResult1 = MockMutationResult<T>, TResult2 = never>(
    onfulfilled?: ((value: MockMutationResult<T>) => TResult1 | PromiseLike<TResult1>) | null,
  ): PromiseLike<TResult1 | TResult2> {
    const result: MockMutationResult<T> = {
      data: Array.isArray(this.data)
        ? this.data.map((item: Record<string, unknown>, i: number) => ({
            ...item,
            id: item?.id ?? `generated-${Date.now()}-${i}`,
          })) as unknown as T[]
        : this.data
          ? [{ ...(this.data as Record<string, unknown>), id: (this.data as Record<string, unknown>).id ?? `generated-${Date.now()}` }] as unknown as T[]
          : null,
      error: null,
    };
    return Promise.resolve(onfulfilled ? onfulfilled(result) : (result as unknown as TResult1));
  }
}

class MockDeleteBuilder {
  eq(_column: string, _value: unknown) {
    return this;
  }
  in(_column: string, _values: unknown[]) {
    return this;
  }
  then(resolve: (value: unknown) => void) {
    resolve({ data: null, error: null });
  }
}

class MockStorageBuilder {
  from(_bucket: string) {
    return {
      upload: async (_path: string, _file: unknown) => ({ data: { path: _path }, error: null }),
      download: async (_path: string) => ({ data: new Blob(), error: null }),
      remove: async (_paths: string[]) => ({ data: { paths: _paths }, error: null }),
      list: async () => ({ data: [], error: null }),
    };
  }
}

// Auth credential types
interface SignInCredentials {
  email?: string;
  password?: string;
}

interface UserAttributes {
  email?: string;
  data?: Record<string, unknown>;
}

function createMockClient() {
  // The query builder is mutable, so we can't simply return a class
  // instance + spread mutation methods. The cleanest way to support
  // both `.from(t).select(...)...` (read) and `.from(t).update(d).eq(...)`
  // (write) in a single chain is to delegate everything to one object
  // and inspect `this.mode` lazily. We build that object via
  // `Object.assign` to keep the TypeScript inference happy.
  function makeFrom<T extends Record<string, unknown> = Record<string, unknown>>(table: string) {
    const qb = new MockQueryBuilder<T>(table);
    return Object.assign(qb, {
      insert: (data: Record<string, unknown>) => new MockMutationBuilder<T>(table, data),
      update: (data: Record<string, unknown>) => new MockMutationBuilder<T>(table, data),
      upsert: (data: Record<string, unknown>) => new MockMutationBuilder<T>(table, data),
      delete: () => new MockDeleteBuilder(),
    });
  }

  return {
    from: makeFrom,
    storage: new MockStorageBuilder(),
    auth: {
      getSession: async () => ({ data: { session: null }, error: null }),
      getUser: async () => ({ data: { user: null }, error: null }),
      signInWithPassword: async (_creds: SignInCredentials) => ({
        data: { user: null, session: null },
        error: null,
      }),
      signOut: async () => ({ error: null }),
      updateUser: async (_attrs: UserAttributes): Promise<{ data: { user: null }; error: null }> => ({
        data: { user: null },
        error: null,
      }),
      onAuthStateChange: () => ({
        data: { subscription: { unsubscribe: () => {} } },
      }),
    },
    rpc: async (_name: string, _params?: Record<string, unknown>): Promise<{ data: null; error: null }> => ({
      data: null,
      error: null,
    }),
    channel: () => ({
      on: () => ({ subscribe: () => ({}) }),
      subscribe: () => ({}),
    }),
  };
}

export const supabase = createMockClient();
