import apiClient from './client';
import { auth, dataConnect } from '../firebase';
import { signOut } from 'firebase/auth';

import * as dcSdk from '@dataconnect/generated'; // listEvents, createEvent, etc.

const MOCK_USER_ROLE_KEY = 'krow_mock_user_role'; 

// --- Auth Module ---
const authModule = {
  /**
   * Fetches the currently authenticated user's profile from the backend.
   * @returns {Promise<object>} The user profile.
   */
  me: async () => {
    // 1. Firebase auth user
    const fbUser = auth.currentUser;

    if (!fbUser) {
      return null; // NO ESTÁ LOGGEADO
    }

    // 2. Attempt to load matching Krow User from DataConnect
    // (because your Krow user metadata is stored in the "users" table)
    let krowUser = null;
    try {
      const response = await dcSdk.getUserById(dataConnect, { id: fbUser.uid });
      krowUser = response.data?.user || null;
    } catch (err) {
      console.warn("Krow user not found in DataConnect, returning Firebase-only info.");
    }

    let mockRole = null;
    mockRole = localStorage.getItem(MOCK_USER_ROLE_KEY);

    // 3. Build unified "me" object
    return {
      id: fbUser.uid,
      email: fbUser.email,
      fullName: krowUser?.fullName || fbUser.displayName || null,
      role: krowUser?.role || "user",
      user_role: mockRole || krowUser?.userRole || null,
      firebase: fbUser,
      krow: krowUser
    };
  },

  /**
   * Logs the user out.
   * @param {string} [redirectUrl] - Optional URL to redirect to after logout.
   */
  logout: async (redirectUrl) => {
    
    localStorage.removeItem(MOCK_USER_ROLE_KEY);

    await signOut(auth);
    if (redirectUrl) {
      window.location.href = redirectUrl;
    }
  },

  /**
   * Checks if a user is currently authenticated.
   * @returns {boolean} True if a user is authenticated.
   */
  isAuthenticated: () => {
    return !!auth.currentUser;
  },

  // ==============================
  // FIX: auth.updateMe para soportar RoleSwitcher (antes lo hacía el mock base44)
  // ==============================
  /**
   * Updates current user metadata (including role/user_role).
   * Used by RoleSwitcher to change between ADMIN / VENDOR / etc.
   * @param {{ user_role?: string, role?: string, fullName?: string }} data
   * @returns {Promise<object>} updated "me" object
   */
  updateMe: async (data) => {
    const fbUser = auth.currentUser;
    if (!fbUser) {
      throw new Error("Not authenticated");
    }
    
    if (data.user_role) {
      try {
        localStorage.setItem(MOCK_USER_ROLE_KEY, data.user_role);
      } catch (err) {
        console.warn("Krow user role could not be saved to localStorage.");
      }
    }

    return authModule.me();

  },

};

// --- Core Integrations Module ---
const coreIntegrationsModule = {
  /**
   * Sends an email.
   * @param {object} params - { to, subject, body }
   * @returns {Promise<object>} API response.
   */
  SendEmail: async (params) => {
    const { data } = await apiClient.post('/sendEmail', params);
    return data;
  },

  /**
   * Invokes a large language model.
   * @param {object} params - { prompt, response_json_schema, file_urls }
   * @returns {Promise<object>} API response.
   */
  InvokeLLM: async (params) => {
    const { data } = await apiClient.post('/invokeLLM', params);
    return data;
  },

  /**
   * Uploads a public file.
   * @param {File} file - The file to upload.
   * @returns {Promise<object>} API response with file_url.
   */
  UploadFile: async ({ file }) => {
    const formData = new FormData();
    formData.append('file', file);
    const { data } = await apiClient.post('/uploadFile', formData, {
      headers: { 'Content-Type': 'multipart/form-data' },
    });
    return data;
  },

  /**
   * Uploads a private file.
   * @param {File} file - The file to upload.
   * @returns {Promise<object>} API response with file_uri.
   */
  UploadPrivateFile: async ({ file }) => {
    const formData = new FormData();
    formData.append('file', file);
    const { data } = await apiClient.post('/uploadPrivateFile', formData, {
      headers: { 'Content-Type': 'multipart/form-data' },
    });
    return data;
  },

  /**
   * Creates a temporary signed URL for a private file.
   * @param {object} params - { file_uri, expires_in }
   * @returns {Promise<object>} API response with signed_url.
   */
  CreateFileSignedUrl: async (params) => {
    const { data } = await apiClient.post('/createSignedUrl', params);
    return data;
  },
};

const dataconnectEntityConfig = {
  User: {
    list: 'listUsers',
    get: 'getUserById',
    create: 'createUser',
    update: 'updateUser',
    delete: 'deleteUser',
    filter: 'filterUsers',
  },
  Event: {
    list: 'listEvents',
    create: 'createEvent',
    get: 'getEventById',
    update: 'updateEvent',
    delete: 'deleteEvent',
    filter: 'filterEvents',
  },

  Staff: {
    list: 'listStaff',
    create: 'createStaff',
    get: 'getStaffById',
    update: 'updateStaff',
    delete: 'deleteStaff',
    filter: 'filterStaff',
  },

  Vendor: {
    list: 'listVendor',
    get: 'getVendorById',
    create: 'createVendor',
    update: 'updateVendor',
    delete: 'deleteVendor',
    filter: 'filterVendors',
  },

  VendorRate: {
    list: 'listVendorRate',
    get: 'getVendorRateById',
    create: 'createVendorRate',
    update: 'updateVendorRate',
    delete: 'deleteVendorRate',
    filter: 'filterVendorRates',
  },
  
  VendorDefaultSetting:{
    list: 'listVendorDefaultSettings',
    get: 'getVendorDefaultSettingById',
    create: 'createVendorDefaultSetting',
    update: 'updateVendorDefaultSetting',
    delete: 'deleteVendorDefaultSetting',
    filter: 'filterVendorDefaultSettings',
  },

  Invoice:{
    list: 'listInvoice',
    get: 'getInvoiceById',
    create: 'createInvoice',
    update: 'updateInvoice',
    delete: 'deleteInvoice',
    filter: 'filterInvoices',

  },
  
  Business:{
    list: 'listBusiness',
    get: 'getBusinessById',
    create: 'createBusiness',
    update: 'updateBusiness',
    delete: 'deleteBusiness',
    filter: 'filterBusiness',
  },

  Certification:{
    list: 'listCertification',
    get: 'getCertificationById',
    create: 'createCertification',
    update: 'updateCertification',
    delete: 'deleteCertification',
    filter: 'filterCertification',
  },
  
  Team:{
    list: 'listTeam',
    get: 'getTeamById',
    create: 'createTeam',
    update: 'updateTeam',
    delete: 'deleteTeam',
    filter: 'filterTeam',
  },

  TeamMember: {
    list: 'listTeamMember',
    get: 'getTeamMemberById',
    create: 'createTeamMember',
    update: 'updateTeamMember',
    delete: 'deleteTeamMember',
    filter: 'filterTeamMember',
  },

  TeamHub: {
    list: 'listTeamHub',
    get: 'getTeamHubById',
    create: 'createTeamHub',
    update: 'updateTeamHub',
    delete: 'deleteTeamHub',
    filter: 'filterTeamHub',
  },

  TeamMemberInvite: {
    list: 'listTeamMemberInvite',
    get: 'getTeamMemberInviteById',
    create: 'createTeamMemberInvite',
    update: 'updateTeamMemberInvite',
    delete: 'deleteTeamMemberInvite',
    filter: 'filterTeamMemberInvite',
  },
  
  Conversation:{
    list: 'listConversation',
    get: 'getConversationById',
    create: 'createConversation',
    update: 'updateConversation',
    delete: 'deleteConversation',
    filter: 'filterConversation',
  }, 
  
  Message:{
    list: 'listMessage',
    get: 'getMessageById',
    create: 'createMessage',
    update: 'updateMessage',
    delete: 'deleteMessage',
    filter: 'filterMessage',
  }, 
  
  ActivityLog:{
    list: 'listActivityLog',
    get: 'getActivityLogById',
    create: 'createActivityLog',
    update: 'updateActivityLog',
    delete: 'deleteActivityLog',
    filter: 'filterActivityLog',
  },

  Enterprise:{
    list: 'listEnterprise',
    get: 'getEnterpriseById',
    create: 'createEnterprise',
    update: 'updateEnterprise',
    delete: 'deleteEnterprise',
    filter: 'filterEnterprise',
  }, 
  
  Sector:{
    list: 'listSector',
    get: 'getSectorById',
    create: 'createSector',
    update: 'updateSector',
    delete: 'deleteSector',
    filter: 'filterSector'
  },
  
  Partner:{
    list: 'listPartner',
    get: 'getPartnerById',
    create: 'createPartner',
    update: 'updatePartner',
    delete: 'deletePartner',
    filter: 'filterPartner'
  }, 
  
  Order:{
    list: 'listOrder',
    get: 'getOrderById',
    create: 'createOrder',
    update: 'updateOrder',
    delete: 'deleteOrder',
    filter: 'filterOrder'
  }, 
  
  Shift:{
    list: 'listShift',
    get: 'getShiftById',
    create: 'createShift',
    update: 'updateShift',
    delete: 'deleteShift',
    filter: 'filterShift'
  }
  
};

// Helper for methods not implemented 
const notImplemented = (entityName, method) => async () => {
  throw new Error(`${entityName}.${method} is not implemented yet for Data Connect`);
};

// helper para normalizar siempre a array (list + filter devolverán arrays)
const normalizeResultToArray = (res) => {
  // if it is an array, perfect
  if (Array.isArray(res)) return res;
  if (!res || typeof res !== 'object') return [];

  const data = res.data;
  if (data && typeof data === 'object') {
    const keys = Object.keys(data);
    if (keys.length === 0) return [];
    const firstValue = data[keys[0]];

    if (Array.isArray(firstValue)) return firstValue;
    if (firstValue == null) return [];
    // if it is a single object, return it as an array
    return [firstValue];
  }

  return [];
};

// --- Entities Module ( Data Connect, without REST Base44) ---
const entitiesModule = {};

// --- Helper to convert snake_case to camelCase recursively ---
const toCamel = (value) => {
  if (Array.isArray(value)) {
    return value.map(toCamel);
  }
  if (value && typeof value === "object") {
    return Object.entries(value).reduce((acc, [key, val]) => {
      const camelKey = key.replace(/_([a-z0-9])/g, (_, c) => c.toUpperCase());
      acc[camelKey] = toCamel(val);
      return acc;
    }, {});
  }
  return value;
};

// --- Helper to convert camelCase to snake_case recursively ---
const toSnake = (value) => {
  if (Array.isArray(value)) {
    return value.map(toSnake);
  }
  if (value && typeof value === "object") {
    return Object.entries(value).reduce((acc, [key, val]) => {
      const snakeKey = key
        .replace(/([A-Z])/g, "_$1")
        .replace(/([a-z])([0-9]+)/g, "$1_$2")
        .toLowerCase();
      acc[snakeKey] = toSnake(val);
      return acc;
    }, {});
  }
  return value;
};


Object.entries(dataconnectEntityConfig).forEach(([entityName, ops]) => {
  entitiesModule[entityName] = {
    
    get: notImplemented(entityName, 'get'),
    update: notImplemented(entityName, 'update'),
    delete: notImplemented(entityName, 'delete'),
    filter: notImplemented(entityName, 'filter'),
    list: notImplemented(entityName, 'list'),
    create: notImplemented(entityName, 'create'),

    // list
    ...(ops.list && {
      list: async (...args) => {
        /*const fn = dcSdk[ops.list];
        if (typeof fn !== 'function') {
          throw new Error(
            `Data Connect operation "${ops.list}" not found for entity "${entityName}".`
          );
        }

        let sort;
        let limit;
        let baseVariables; // por si algún list usa variables (como Team)

        if (args.length === 1) {
          const [a0] = args;
          if (typeof a0 === "string") {
            // list('-created_date')
            sort = a0;
          } else if (a0 && typeof a0 === "object" && !Array.isArray(a0)) {
            // list({ ...vars }) -> reservado para queries que acepten variables
            baseVariables = a0;
          }
        } else if (args.length === 2) {
          const [a0, a1] = args;
          if (typeof a0 === "string" && typeof a1 === "number") {
            // list('-created_date', 50)
            sort = a0;
            limit = a1;
          } else if (a0 && typeof a0 === "object" && !Array.isArray(a0)) {
            // list({ ...vars }, '-created_date')
            baseVariables = a0;
            if (typeof a1 === "string") sort = a1;
          }
        } else if (args.length >= 3) {
          const [a0, a1, a2] = args;
          if (a0 && typeof a0 === "object" && !Array.isArray(a0)) {
            // list({ ...vars }, '-created_date', 50)
            baseVariables = a0;
            if (typeof a1 === "string") sort = a1;
            if (typeof a2 === "number") limit = a2;
          }
        }

        let variables = baseVariables;

        if (entityName === "Team") {
          variables = variables || {};
          if (sort) {
            const desc = sort.startsWith("-");
            variables.orderByCreatedDate = desc ? "DESC" : "ASC";
          }
          if (typeof limit === "number") {
            variables.limit = limit;
          }
        }

        const res = await fn(dataConnect, variables);
        let items = normalizeResultToArray(res);

        // para entidades que NO tienen orderBy/limit en el query,
        // aplicamos sort/limit en el front como fallback.
        if (entityName !== "Team" && sort) {
          const desc = sort.startsWith("-");
          const field = desc ? sort.slice(1) : sort; // '-created_date' -> 'created_date'

          items = items.slice().sort((a, b) => {
            const av = a?.[field];
            const bv = b?.[field];

            if (av == null && bv == null) return 0;
            if (av == null) return 1;
            if (bv == null) return -1;

            const da = new Date(av);
            const db = new Date(bv);
            if (!isNaN(da) && !isNaN(db)) {
              return desc ? db - da : da - db;
            }

            if (av < bv) return desc ? 1 : -1;
            if (av > bv) return desc ? -1 : 1;
            return 0;
          });
        }

      if (entityName !== "Team" && typeof limit === "number") {
        items = items.slice(0, limit);
      }
      //console.log(items)
      //return items;
    */
      const fn = dcSdk[ops.list];
             if (typeof fn !== 'function') {
               throw new Error(
                 `Data Connect operation "${ops.list}" not found for entity "${entityName}".`
               );
             }
    try {
            // --- Lógica de parseo de argumentos (se mantiene para compatibilidad) ---
            let sort;
            let limit;
            let baseVariables;
    
            if (args.length === 1) {
              const [a0] = args;
              if (typeof a0 === "string") sort = a0;
              else if (a0 && typeof a0 === "object") baseVariables = a0;
            } else if (args.length === 2) {
              const [a0, a1] = args;
              if (typeof a0 === "string" && typeof a1 === "number") {
                sort = a0;
                limit = a1;
              } else if (a0 && typeof a0 === "object") {
                baseVariables = a0;
                if (typeof a1 === "string") sort = a1;
              }
            } else if (args.length >= 3) {
              const [a0, a1, a2] = args;
              if (a0 && typeof a0 === "object") {
                baseVariables = a0;
                if (typeof a1 === "string") sort = a1;
                if (typeof a2 === "number") limit = a2;
              }
            }
    
            // --- FIX: Se llama al backend SIN parámetros de orden/límite ---
            // Se asume que la mayoría de queries 'list' no aceptan variables, solo se pasan si existen.
            
            //const res = await fn(dataConnect, baseVariables);
            const res = await fn(dataConnect, baseVariables);

            let items = normalizeResultToArray(res);
    
            // --- FIX: SE RESTAURA el ordenamiento y límite en el CLIENTE ---
            // Esto es necesario hasta que el backend pueda manejar estos parámetros.
            if (sort) {
              const isDesc = sort.startsWith("-");
              const field = isDesc ? sort.slice(1) : sort;
              
              items = items.slice().sort((a, b) => {
                const av = a?.[field];
                const bv = b?.[field];
    
                if (av == null && bv == null) return 0;
                if (av == null) return 1;
                if (bv == null) return -1;
    
                const da = new Date(av);
                const db = new Date(bv);
                if (!isNaN(da) && !isNaN(db)) {
                  return isDesc ? db - da : da - db;
                }
    
                if (av < bv) return isDesc ? 1 : -1;
                if (av > bv) return isDesc ? -1 : 1;
                return 0;
              });
            }
    
            if (typeof limit === "number") {
              items = items.slice(0, limit);
            }
      return items.map(toSnake);
      } catch (err) {
      console.error('list staff failed', err);
      throw err;
    }
      },
    }),

    // create
    ...(ops.create && {
      create: async (params) => {
        const fn = dcSdk[ops.create];
        if (typeof fn !== 'function') {
          throw new Error(
            `Data Connect operation "${ops.create}" not found for entity "${entityName}".`
          );
        }

        if (!params || typeof params !== 'object') {
          throw new Error(
            `${entityName}.create expects an object with the fields to insert`
          );
        }

        let payload;

        if (params.data && typeof params.data === 'object') {
          // caso nuevo: create({ data: { ... } })
          payload = params.data;
        } else {
          // caso legacy: create({ ...fields })
          payload = params;
        }

        //converting to camelCase for data connect
        payload = toCamel(payload);

        return fn(dataConnect, payload);
      },
    }),

    //get
    ...(ops.get && {
      get: async (params) => {
        const fn = dcSdk[ops.get];
        if (typeof fn !== 'function') {
          throw new Error(
            `Data Connect operation "${ops.get}" not found for entity "${entityName}".`
          );
        }

        if (!params || typeof params !== 'object') {
          throw new Error(`${entityName}.get expects an object of variables (e.g. { id })`);
        }

        //return fn(dataConnect, params);
        const result = await fn(dataConnect, params);
        return toSnake(result);

      },
    }),
    
    //update
    ...(ops.update && {
      update: async (id,params) => {
        const fn = dcSdk[ops.update];
        if (typeof fn !== 'function') {
          throw new Error(
            `Data Connect operation "${ops.update}" not found for entity "${entityName}".`
          );
        }

        if (!params || typeof params !== 'object') {
          throw new Error(
            `${entityName}.update expects an object of variables matching the GraphQL mutation`
          );
        }

        //const { id, data } = params;
        let data = params;

        if (!id) {
          throw new Error(`${entityName}.update requires an "id" field`);
        }

        if (!data || typeof data !== 'object') {
          throw new Error(
            `${entityName}.update requires a "data" object with the fields to update`
          );
        }
        
        if (data && typeof data === "object") {
          data = toCamel(data);
        }
        const vars = { id, ...data };

        return fn(dataConnect, vars);

      },
    }),
    
    
    // delete
    ...(ops.delete && {
      delete: async (params) => {
        const fn = dcSdk[ops.delete];
        if (typeof fn !== 'function') {
          throw new Error(
            `Data Connect operation "${ops.delete}" not found for entity "${entityName}".`
          );
        }

        if (!params || typeof params !== 'object') {
          throw new Error(
            `${entityName}.delete expects an object like { id }`
          );
        }

        const { id } = params;

        if (!id) {
          throw new Error(`${entityName}.delete requires an "id" field`);
        }

        // Data Connect solo espera { id } como variables
        return fn(dataConnect, { id });
      },
    }),


    // filter
    ...(ops.filter && {
      filter: async (...args) => {
        const fn = dcSdk[ops.filter];
        if (typeof fn !== 'function') {
          throw new Error(
            `Data Connect operation "${ops.filter}" not found for entity "${entityName}".`
          );
        }

        // FIX: soportar firma vieja: filter(filters, sort, limit)
        const [params, sort, limit] = args;
        // FIX: soportar firma vieja: filter(filters, sort, limit)

        if (!params) {
          if (ops.list) {//if no params, call to list()
            const listFn = dcSdk[ops.list];
            if (typeof listFn !== 'function') {
              throw new Error(
                `Data Connect operation "${ops.list}" not found for entity "${entityName}".`
              );
            }
            //return listFn(dataConnect);
            //fix
            const resList = await listFn(dataConnect);
            return normalizeResultToArray(resList);
            //fix
          }
          throw new Error(`${entityName}.filter expects params or a list operation`);
        }
        const rawFilters = params.filters ?? params;
        const variables = {};

        for (const [key, value] of Object.entries(rawFilters)) {//cleaning undefined/null/'' values
          if (value !== undefined && value !== null && value !== '') {
            variables[key] = value;
          }
        }

        // if no valid filters, call to list()
        if (Object.keys(variables).length === 0) {
          if (ops.list) {
            const listFn = dcSdk[ops.list];
            if (typeof listFn !== 'function') {
              throw new Error(
                `Data Connect operation "${ops.list}" not found for entity "${entityName}".`
              );
            }
            //return listFn(dataConnect);
            //fix
            const resList = await listFn(dataConnect);
            return normalizeResultToArray(resList);
            //fix
          }
          throw new Error(`${entityName}.filter received no valid filters and no list operation`);
        }

        //return fn(dataConnect, variables);
        //fix
        const res = await fn(dataConnect, variables);

        // FIX: normalizar a array (activityLogs[], messages[], etc.)
        let items = normalizeResultToArray(res);

        // FIX: soportar sort tipo '-created_date' o 'created_date'
        if (sort) {
          const desc = sort.startsWith('-');
          const field = desc ? sort.slice(1) : sort;

          items = items.slice().sort((a, b) => {
            const av = a?.[field];
            const bv = b?.[field];

            if (av == null && bv == null) return 0;
            if (av == null) return 1;
            if (bv == null) return -1;

            // Intentar tratarlos como fecha si parecen fechas
            const da = new Date(av);
            const db = new Date(bv);
            if (!isNaN(da) && !isNaN(db)) {
              return desc ? db - da : da - db;
            }

            // Fallback: comparación normal
            if (av < bv) return desc ? 1 : -1;
            if (av > bv) return desc ? -1 : 1;
            return 0;
          });
        }

        // FIX: soportar limit (ej: 50)
        if (typeof limit === 'number') {
          items = items.slice(0, limit);
        }

        return items;
        //fix
      },
    }),

  };
});


// --- Main SDK Export ---
export const krowSDK = {
  auth: authModule,
  integrations: {
    Core: coreIntegrationsModule,
  },
  entities: entitiesModule,
};
