import { SupabaseClient } from '@supabase/supabase-js';

import { Database } from '@kit/supabase/database';

import PersonalCode from '~/lib/utils';

import { AccountWithParams } from '../types/accounts';

/**
 * Class representing an API for interacting with user accounts.
 * @constructor
 * @param {SupabaseClient<Database>} client - The Supabase client instance.
 */
class AccountsApi {
  constructor(private readonly client: SupabaseClient<Database>) {}

  /**
   * @name getAccount
   * @description Get the account data for the given ID.
   * @param id
   */
  async getAccount(id: string): Promise<AccountWithParams> {
    const { data, error } = await this.client
      .schema('medreport')
      .from('accounts')
      .select(
        '*, accountParams: account_params (weight, height, isSmoker:is_smoker)',
      )
      .eq('id', id)
      .single();

    if (error) {
      throw error;
    }

    return data;
  }

  /**
   * @name getPersonalAccountByUserId
   * @description Get the personal account data for the given user ID.
   * @param userId
   */
  async getPersonalAccountByUserId(userId: string): Promise<AccountWithParams> {
    const { data, error } = await this.client
      .schema('medreport')
      .from('accounts')
      .select(
        '*, accountParams: account_params (weight, height, isSmoker:is_smoker)',
      )
      .eq('primary_owner_user_id', userId)
      .eq('is_personal_account', true)
      .single();

    if (error) {
      throw error;
    }

    const { personal_code, ...rest } = data;
    return {
      ...rest,
      personal_code: PersonalCode.getPersonalCode(personal_code),
    };
  }

  /**
   * @name getAccountWorkspace
   * @description Get the account workspace data.
   */
  async getAccountWorkspace() {
    const { data, error } = await this.client
      .schema('medreport')
      .from('user_account_workspace')
      .select(`*`)
      .single();

    if (error) {
      throw error;
    }

    return data;
  }

  /**
   * @name loadUserAccounts
   * Load only user-owned accounts (not just memberships).
   */
  async loadUserAccounts() {
    const authUser = await this.client.auth.getUser();

    const { data, error: userError } = authUser;

    if (userError) {
      console.error('Failed to get user', userError);
      throw userError;
    }

    const { user } = data;

    const { data: accounts, error } = await this.client
      .schema('medreport')
      .from('accounts_memberships')
      .select(
        `
          account_id,
          accounts (
            name,
            slug,
            picture_url,
            application_role
          )
        `,
      )
      .eq('user_id', user.id)
      .eq('account_role', 'owner');

    if (error) {
      console.error('error', error);
      throw error;
    }

    return accounts.map(({ accounts }) => ({
      label: accounts.name,
      value: accounts.slug,
      image: accounts.picture_url,
      application_role: accounts.application_role,
    }));
  }

  async loadTempUserAccounts() {
    const { data: accounts, error } = await this.client
      .schema('medreport')
      .from('user_accounts')
      .select(`name, slug`);

    if (error) {
      throw error;
    }

    return accounts.map(({ name, slug }) => {
      return {
        label: name,
        value: slug,
      };
    });
  }

  /**
   * @name getSubscription
   * Get the subscription data for the given user.
   * @param accountId
   */
  async getSubscription(accountId: string) {
    const response = await this.client
      .schema('medreport')
      .from('subscriptions')
      .select('*, items: subscription_items !inner (*)')
      .eq('account_id', accountId)
      .maybeSingle();

    if (response.error) {
      throw response.error;
    }

    return response.data;
  }

  /**
   * Get the orders data for the given account.
   * @param accountId
   */
  async getOrder(accountId: string) {
    const response = await this.client
      .schema('medreport')
      .from('orders')
      .select('*, items: order_items !inner (*)')
      .eq('account_id', accountId)
      .maybeSingle();

    if (response.error) {
      throw response.error;
    }

    return response.data;
  }

  /**
   * @name getCustomerId
   * Get the billing customer ID for the given user.
   * If the user does not have a billing customer ID, it will return null.
   * @param accountId
   */
  async getCustomerId(accountId: string) {
    const response = await this.client
      .schema('medreport')
      .from('billing_customers')
      .select('customer_id')
      .eq('account_id', accountId)
      .maybeSingle();

    if (response.error) {
      throw response.error;
    }

    return response.data?.customer_id;
  }

  async hasAccountTeamMembership(accountId?: string) {
    if (!accountId) {
      return false;
    }

    const { count, error } = await this.client
      .schema('medreport')
      .from('accounts_memberships')
      .select('account_id', { count: 'exact', head: true })
      .eq('account_id', accountId);

    if (error) {
      throw error;
    }

    return (count ?? 0) > 0;
  }
}

export function createAccountsApi(client: SupabaseClient<Database>) {
  return new AccountsApi(client);
}
