'use server';

import { revalidateTag } from 'next/cache';

import { sdk } from '@lib/config';
import medusaError from '@lib/util/medusa-error';
import { HttpTypes } from '@medusajs/types';

import {
  getAuthHeaders,
  getCacheOptions,
  getCacheTag,
  getCartId,
  removeAuthToken,
  removeCartId,
  setAuthToken,
} from './cookies';

export const retrieveCustomer =
  async (): Promise<HttpTypes.StoreCustomer | null> => {
    const authHeaders = await getAuthHeaders();

    if (!authHeaders) return null;

    const headers = {
      ...authHeaders,
    };

    const next = {
      ...(await getCacheOptions('customers')),
    };

    return await sdk.client
      .fetch<{ customer: HttpTypes.StoreCustomer }>(`/store/customers/me`, {
        method: 'GET',
        query: {
          fields: '*orders',
        },
        headers,
        next,
        cache: 'force-cache',
      })
      .then(({ customer }) => customer)
      .catch(() => null);
  };

export const updateCustomer = async (body: HttpTypes.StoreUpdateCustomer) => {
  const headers = {
    ...(await getAuthHeaders()),
  };

  const updateRes = await sdk.store.customer
    .update(body, {}, headers)
    .then(({ customer }) => customer)
    .catch(medusaError);

  const cacheTag = await getCacheTag('customers');
  revalidateTag(cacheTag);

  return updateRes;
};

export async function signup(_currentState: unknown, formData: FormData) {
  const password = formData.get('password') as string;
  const customerForm = {
    email: formData.get('email') as string,
    first_name: formData.get('first_name') as string,
    last_name: formData.get('last_name') as string,
    phone: formData.get('phone') as string,
  };

  try {
    const token = await sdk.auth.register('customer', 'emailpass', {
      email: customerForm.email,
      password: password,
    });

    await setAuthToken(token as string);

    const headers = {
      ...(await getAuthHeaders()),
    };

    const { customer: createdCustomer } = await sdk.store.customer.create(
      customerForm,
      {},
      headers,
    );

    const loginToken = await sdk.auth.login('customer', 'emailpass', {
      email: customerForm.email,
      password,
    });

    await setAuthToken(loginToken as string);

    const customerCacheTag = await getCacheTag('customers');
    revalidateTag(customerCacheTag);

    await transferCart();

    return createdCustomer;
  } catch (error: any) {
    return error.toString();
  }
}

export async function login(_currentState: unknown, formData: FormData) {
  const email = formData.get('email') as string;
  const password = formData.get('password') as string;

  try {
    await sdk.auth
      .login('customer', 'emailpass', { email, password })
      .then(async (token) => {
        await setAuthToken(token as string);
        const customerCacheTag = await getCacheTag('customers');
        revalidateTag(customerCacheTag);
      });
  } catch (error: any) {
    return error.toString();
  }

  try {
    await transferCart();
  } catch (error: any) {
    return error.toString();
  }
}

export async function medusaLogout(
  countryCode = 'ee',
  canRevalidateTags = true,
) {
  await sdk.auth.logout();

  await removeAuthToken();

  if (canRevalidateTags) {
    const customerCacheTag = await getCacheTag('customers');
    revalidateTag(customerCacheTag);
  }

  await removeCartId();

  if (canRevalidateTags) {
    const cartCacheTag = await getCacheTag('carts');
    revalidateTag(cartCacheTag);
  }
}

export async function transferCart() {
  const cartId = await getCartId();

  if (!cartId) {
    return;
  }

  const headers = await getAuthHeaders();

  await sdk.store.cart.transferCart(cartId, {}, headers);

  const cartCacheTag = await getCacheTag('carts');
  revalidateTag(cartCacheTag);
}

export const addCustomerAddress = async (
  currentState: Record<string, unknown>,
  formData: FormData,
): Promise<any> => {
  const isDefaultBilling = (currentState.isDefaultBilling as boolean) || false;
  const isDefaultShipping =
    (currentState.isDefaultShipping as boolean) || false;

  const address = {
    first_name: formData.get('first_name') as string,
    last_name: formData.get('last_name') as string,
    company: formData.get('company') as string,
    address_1: formData.get('address_1') as string,
    address_2: formData.get('address_2') as string,
    city: formData.get('city') as string,
    postal_code: formData.get('postal_code') as string,
    province: formData.get('province') as string,
    country_code: formData.get('country_code') as string,
    phone: formData.get('phone') as string,
    is_default_billing: isDefaultBilling,
    is_default_shipping: isDefaultShipping,
  };

  const headers = {
    ...(await getAuthHeaders()),
  };

  return sdk.store.customer
    .createAddress(address, {}, headers)
    .then(async ({ customer }) => {
      const customerCacheTag = await getCacheTag('customers');
      revalidateTag(customerCacheTag);
      return { success: true, error: null };
    })
    .catch((err) => {
      return { success: false, error: err.toString() };
    });
};

export const deleteCustomerAddress = async (
  addressId: string,
): Promise<void> => {
  const headers = {
    ...(await getAuthHeaders()),
  };

  await sdk.store.customer
    .deleteAddress(addressId, headers)
    .then(async () => {
      const customerCacheTag = await getCacheTag('customers');
      revalidateTag(customerCacheTag);
      return { success: true, error: null };
    })
    .catch((err) => {
      return { success: false, error: err.toString() };
    });
};

export const updateCustomerAddress = async (
  currentState: Record<string, unknown>,
  formData: FormData,
): Promise<any> => {
  const addressId =
    (currentState.addressId as string) || (formData.get('addressId') as string);

  if (!addressId) {
    return { success: false, error: 'Address ID is required' };
  }

  const address = {
    first_name: formData.get('first_name') as string,
    last_name: formData.get('last_name') as string,
    company: formData.get('company') as string,
    address_1: formData.get('address_1') as string,
    address_2: formData.get('address_2') as string,
    city: formData.get('city') as string,
    postal_code: formData.get('postal_code') as string,
    province: formData.get('province') as string,
    country_code: formData.get('country_code') as string,
  } as HttpTypes.StoreUpdateCustomerAddress;

  const phone = formData.get('phone') as string;

  if (phone) {
    address.phone = phone;
  }

  const headers = {
    ...(await getAuthHeaders()),
  };

  return sdk.store.customer
    .updateAddress(addressId, address, {}, headers)
    .then(async () => {
      const customerCacheTag = await getCacheTag('customers');
      revalidateTag(customerCacheTag);
      return { success: true, error: null };
    })
    .catch((err) => {
      return { success: false, error: err.toString() };
    });
};

async function medusaLogin(email: string, password: string) {
  const token = await sdk.auth.login('customer', 'emailpass', {
    email,
    password,
  });
  await setAuthToken(token as string);

  try {
    await transferCart();
  } catch (e) {
    console.error('Failed to transfer cart', e);
  }

  const customer = await retrieveCustomer();
  if (!customer) {
    throw new Error('Customer not found for active session');
  }

  return customer.id;
}

async function medusaRegister({
  email,
  password,
  name,
  lastName,
}: {
  email: string;
  password: string;
  name: string | undefined;
  lastName: string | undefined;
}) {
  console.info(
    `Creating new Medusa account for Keycloak user with email=${email}`,
  );

  const registerToken = await sdk.auth.register('customer', 'emailpass', {
    email,
    password,
  });
  await setAuthToken(registerToken);

  console.info(
    `Creating new Medusa customer profile for Keycloak user with email=${email} and name=${name} and lastName=${lastName}`,
  );
  await sdk.store.customer.create(
    { email, first_name: name, last_name: lastName },
    {},
    {
      ...(await getAuthHeaders()),
    },
  );
}

export async function medusaLoginOrRegister(
  credentials: {
    email: string;
    supabaseUserId?: string;
    name?: string;
    lastName?: string;
  } & (
    | { isDevPasswordLogin: true; password: string }
    | { isDevPasswordLogin?: false; password?: undefined }
  ),
) {
  const { email, supabaseUserId, name, lastName } = credentials;

  const password = await (async () => {
    if (credentials.isDevPasswordLogin) {
      return credentials.password;
    }

    return generateDeterministicPassword(email, supabaseUserId);
  })();

  try {
    return await medusaLogin(email, password);
  } catch (loginError) {
    console.error(
      'Failed to login customer, attempting to register',
      loginError,
    );

    try {
      await medusaRegister({ email, password, name, lastName });
      return await medusaLogin(email, password);
    } catch (registerError) {
      console.error(
        `Failed to create Medusa account for user with email=${email}`,
        registerError,
      );
      throw medusaError(registerError);
    }
  }
}

/**
 * Generate a deterministic password based on user identifier
 * This ensures the same user always gets the same password for Medusa
 */
async function generateDeterministicPassword(
  email: string,
  userId?: string,
): Promise<string> {
  // Use the user ID or email as the base for deterministic generation
  const baseString = userId || email;
  const secret = process.env.MEDUSA_PASSWORD_SECRET!;

  // Create a deterministic password using HMAC
  const encoder = new TextEncoder();
  const keyData = encoder.encode(secret);
  const messageData = encoder.encode(baseString);

  // Import key for HMAC
  const key = await crypto.subtle.importKey(
    'raw',
    keyData,
    { name: 'HMAC', hash: 'SHA-256' },
    false,
    ['sign'],
  );
  // Generate HMAC
  const signature = await crypto.subtle.sign('HMAC', key, messageData);
  // Convert to base64 and make it a valid password
  const hashArray = Array.from(new Uint8Array(signature));
  const hashHex = hashArray
    .map((b) => b.toString(16).padStart(2, '0'))
    .join('');
  // Take first 24 characters and add some complexity
  const basePassword = hashHex.substring(0, 24);
  // Add some required complexity for Medusa (uppercase, lowercase, numbers, symbols)
  return `Mk${basePassword}9!`;
}
