import type { Member, MembershipStatus, MemberResponse, NocoDBSettings } from '~/utils/types';

// Data normalization functions
export const normalizePersonName = (name: string): string => {
  if (!name) return 'Unknown';
  
  // Trim whitespace and normalize case
  return name.trim()
    .split(' ')
    .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
    .join(' ');
};

// Pagination interface
export interface PageInfo {
  pageSize: number;
  totalRows: number;
  isFirstPage: boolean;
  isLastPage: boolean;
  page: number;
}

// Response interfaces
export interface EntityResponse<T> {
  list: T[];
  PageInfo: PageInfo;
}

// Dynamic table ID getter - will use configured table ID from admin panel
export const getTableId = (tableName: 'Members'): string => {
  // Try to get table ID from global configuration first
  if (globalNocoDBConfig?.tables && tableName === 'Members') {
    const tableId = globalNocoDBConfig.tables['members'] || globalNocoDBConfig.tables['Members'];
    if (tableId) {
      console.log(`[nocodb] Using global table ID for ${tableName}:`, tableId);
      return tableId;
    }
  }
  
  // Fallback to default
  const defaultTableId = 'members-table-id';
  console.log(`[nocodb] Using fallback table ID for ${tableName}:`, defaultTableId);
  return defaultTableId;
};

// Table ID enumeration - Replace with your actual table IDs
export enum Table {
  Members = "members-table-id", // Will be configured via admin panel
}

/**
 * Convert date from DD-MM-YYYY format to YYYY-MM-DD format for PostgreSQL
 */
const convertDateFormat = (dateString: string): string => {
  if (!dateString) return dateString;
  
  // If it's already in ISO format or contains 'T', return as is
  if (dateString.includes('T') || dateString.match(/^\d{4}-\d{2}-\d{2}/)) {
    return dateString;
  }
  
  // Handle DD-MM-YYYY format
  const ddmmyyyyMatch = dateString.match(/^(\d{1,2})-(\d{1,2})-(\d{4})$/);
  if (ddmmyyyyMatch) {
    const [, day, month, year] = ddmmyyyyMatch;
    const convertedDate = `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`;
    console.log(`[convertDateFormat] Converted ${dateString} to ${convertedDate}`);
    return convertedDate;
  }
  
  // Handle DD/MM/YYYY format
  const ddmmyyyySlashMatch = dateString.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/);
  if (ddmmyyyySlashMatch) {
    const [, day, month, year] = ddmmyyyySlashMatch;
    const convertedDate = `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`;
    console.log(`[convertDateFormat] Converted ${dateString} to ${convertedDate}`);
    return convertedDate;
  }
  
  console.warn(`[convertDateFormat] Could not parse date format: ${dateString}`);
  return dateString;
};

// String data handling functions
export const parseStringBoolean = (value: string): boolean => {
  return value === 'true';
};

export const formatBooleanAsString = (value: boolean): string => {
  return value ? 'true' : 'false';
};

export const parseNationalities = (nationalityString: string): string[] => {
  return nationalityString ? nationalityString.split(',').map(n => n.trim()).filter(n => n.length > 0) : [];
};

export const formatNationalitiesAsString = (nationalities: string[]): string => {
  return nationalities.filter(n => n && n.trim()).join(',');
};

// Field normalization functions
export const normalizeFieldsFromNocoDB = (data: any): Member => {
  console.log('[normalizeFieldsFromNocoDB] Input data keys:', Object.keys(data));
  
  // Since NocoDB schema uses snake_case directly, no complex mapping needed
  const normalized: any = { ...data };
  
  // Ensure all expected fields exist with proper fallbacks
  const requiredFields = [
    'first_name', 'last_name', 'email', 'phone', 'nationality', 
    'address', 'date_of_birth', 'membership_status', 'member_since',
    'member_id', 'current_year_dues_paid', 'membership_date_paid',
    'payment_due_date', 'keycloak_id'
  ];
  
  // Initialize missing fields as empty strings (except booleans)
  requiredFields.forEach(field => {
    if (!(field in normalized) || normalized[field] === null || normalized[field] === undefined) {
      if (field === 'current_year_dues_paid') {
        normalized[field] = 'false'; // Boolean field default
      } else if (field === 'membership_status') {
        normalized[field] = 'Pending'; // Enum field default
      } else {
        normalized[field] = ''; // String field default
      }
    }
  });
  
  console.log('[normalizeFieldsFromNocoDB] Normalized member fields:', Object.keys(normalized));
  console.log('[normalizeFieldsFromNocoDB] Final first_name:', `"${normalized.first_name}"`);
  console.log('[normalizeFieldsFromNocoDB] Final last_name:', `"${normalized.last_name}"`);
  
  return normalized as Member;
};

export const normalizeFieldsForNocoDB = (data: any): Record<string, any> => {
  console.log('[normalizeFieldsForNocoDB] Input data keys:', Object.keys(data));
  
  // Field mapping for snake_case to display names (WRITE operations)
  const writeFieldMap: Record<string, string> = {
    'first_name': 'First Name',
    'last_name': 'Last Name', 
    'email': 'Email',
    'phone': 'Phone',
    'date_of_birth': 'Date of Birth',
    'nationality': 'Nationality',
    'address': 'Address',
    'membership_status': 'Membership Status',
    'member_since': 'Member Since',
    'member_id': 'Member ID', // Added field mapping for member_id
    'current_year_dues_paid': 'Current Year Dues Paid',
    'membership_date_paid': 'Membership Date Paid',
    'payment_due_date': 'Payment Due Date',
    'keycloak_id': 'Keycloak ID'
  };
  
  const normalized: any = {};
  
  // First, try direct mapping using write field map
  for (const [sourceKey, targetKey] of Object.entries(writeFieldMap)) {
    if (sourceKey in data && data[sourceKey] !== undefined) {
      normalized[targetKey] = data[sourceKey];
      console.log(`[normalizeFieldsForNocoDB] Mapped "${sourceKey}" -> "${targetKey}":`, data[sourceKey]);
    }
  }
  
  // If no mappings worked, try to use the data as-is (fallback)
  if (Object.keys(normalized).length === 0) {
    console.log('[normalizeFieldsForNocoDB] No field mappings applied, using original data');
    return { ...data };
  }
  
  console.log('[normalizeFieldsForNocoDB] Final normalized keys:', Object.keys(normalized));
  return normalized;
};

// Global variable to store effective configuration
let globalNocoDBConfig: any = null;

// Function to set the global configuration (called by the admin-config system)
export const setGlobalNocoDBConfig = (config: any) => {
  globalNocoDBConfig = config;
  console.log('[nocodb] Global configuration updated:', config.url);
};

export const getNocoDbConfiguration = () => {
  let configToUse: any = null;
  
  // 1. PRIORITY: Check for environment variables first (container-friendly)
  const envConfig = {
    url: process.env.NUXT_NOCODB_URL || process.env.NOCODB_URL,
    token: process.env.NUXT_NOCODB_TOKEN || process.env.NOCODB_TOKEN || process.env.NOCODB_API_TOKEN,
    baseId: process.env.NUXT_NOCODB_BASE_ID || process.env.NOCODB_BASE_ID
  };
  
  if (envConfig.url && envConfig.token) {
    console.log('[nocodb] ✅ Using environment variables - URL:', envConfig.url);
    configToUse = envConfig;
  }
  // 2. FALLBACK: Try to use the global admin panel configuration 
  else if (globalNocoDBConfig) {
    console.log('[nocodb] Using admin panel configuration - URL:', globalNocoDBConfig.url);
    configToUse = {
      url: globalNocoDBConfig.url,
      token: globalNocoDBConfig.token,
      baseId: globalNocoDBConfig.baseId
    };
  }
  // 3. LAST RESORT: Runtime config
  else {
    console.log('[nocodb] ⚠️ Using fallback runtime config');
    const config = useRuntimeConfig().nocodb;
    configToUse = {
      ...config,
      url: config.url || 'https://database.monacousa.org'
    };
    console.log('[nocodb] Fallback configuration URL:', configToUse.url);
  }
  
  // Validate configuration completeness
  if (!configToUse.url || !configToUse.token) {
    console.error('[nocodb] ❌ CRITICAL ERROR: Incomplete NocoDB configuration!');
    console.error('[nocodb] URL:', configToUse.url ? 'SET' : 'MISSING');
    console.error('[nocodb] Token:', configToUse.token ? 'SET' : 'MISSING');
    console.error('[nocodb] Set environment variables: NUXT_NOCODB_URL, NUXT_NOCODB_TOKEN');
    throw createError({
      statusCode: 500,
      statusMessage: 'NocoDB configuration incomplete. Set NUXT_NOCODB_URL and NUXT_NOCODB_TOKEN environment variables.'
    });
  }
  
  // Validate API token before using it
  const token = configToUse.token.trim();
  
  // Check for non-ASCII characters that would cause ByteString errors
  if (!/^[\x00-\xFF]*$/.test(token)) {
    console.error('[nocodb] ❌ CRITICAL ERROR: API token contains invalid Unicode characters!');
    console.error('[nocodb] This will cause ByteString conversion errors in HTTP headers.');
    throw createError({
      statusCode: 500,
      statusMessage: 'NocoDB API token contains invalid characters. Please set a valid NUXT_NOCODB_TOKEN environment variable.'
    });
  }
  
  // Additional validation for common token issues
  if (token.includes('•') || token.includes('…') || token.includes('"') || token.includes('"')) {
    console.error('[nocodb] ❌ CRITICAL ERROR: API token contains formatting characters!');
    console.error('[nocodb] Found characters like bullets (•), quotes, etc. that break HTTP headers.');
    throw createError({
      statusCode: 500,
      statusMessage: 'NocoDB API token contains formatting characters. Please set a clean NUXT_NOCODB_TOKEN environment variable.'
    });
  }
  
  console.log('[nocodb] ✅ Configuration validated successfully');
  return configToUse;
};

export const createTableUrl = (table: Table | string) => {
  let tableId: string;
  
  if (table === Table.Members || table === 'Members') {
    tableId = getTableId('Members');
  } else {
    tableId = table.toString();
  }
  
  const url = `${getNocoDbConfiguration().url}/api/v2/tables/${tableId}/records`;
  console.log('[nocodb] Table URL:', url);
  return url;
};

// CRUD operations for Members table
export const getMembers = async (): Promise<EntityResponse<Member>> => {
  console.log('[nocodb.getMembers] Fetching all members...');
  const startTime = Date.now();
  
  try {
    const result = await $fetch<EntityResponse<Member>>(createTableUrl(Table.Members), {
      headers: {
        "xc-token": getNocoDbConfiguration().token,
      },
      params: {
        limit: 1000,
      },
    });

    console.log('[nocodb.getMembers] Successfully fetched members, count:', result.list?.length || 0);
    console.log('[nocodb.getMembers] Request duration:', Date.now() - startTime, 'ms');
    
    // DIAGNOSTIC: Log raw member data structure to identify schema issues
    if (result.list && result.list.length > 0) {
      const sampleMember = result.list[0];
      console.log('[nocodb.getMembers] DIAGNOSTIC - Raw member fields from NocoDB:', Object.keys(sampleMember));
      console.log('[nocodb.getMembers] DIAGNOSTIC - Sample member data:', JSON.stringify(sampleMember, null, 2));
      console.log('[nocodb.getMembers] DIAGNOSTIC - first_name value:', sampleMember.first_name);
      console.log('[nocodb.getMembers] DIAGNOSTIC - last_name value:', sampleMember.last_name);
      console.log('[nocodb.getMembers] DIAGNOSTIC - First Name value:', (sampleMember as any)['First Name']);
      console.log('[nocodb.getMembers] DIAGNOSTIC - Last Name value:', (sampleMember as any)['Last Name']);
    }
    
    return result;
  } catch (error: any) {
    console.error('[nocodb.getMembers] Error fetching members:', error);
    throw error;
  }
};

export const getMemberById = async (id: string): Promise<Member> => {
  console.log('[nocodb.getMemberById] Fetching member ID:', id);
  
  const result = await $fetch<Member>(`${createTableUrl(Table.Members)}/${id}`, {
    headers: {
      "xc-token": getNocoDbConfiguration().token,
    },
  });
  
  console.log('[nocodb.getMemberById] Successfully retrieved member:', result.Id);
  return result;
};

export const getMemberByKeycloakId = async (keycloakId: string): Promise<Member | null> => {
  console.log('[nocodb.getMemberByKeycloakId] Searching for member with Keycloak ID:', keycloakId);
  
  try {
    // Use the where parameter to search by keycloak_id
    const result = await $fetch<EntityResponse<Member>>(createTableUrl(Table.Members), {
      headers: {
        "xc-token": getNocoDbConfiguration().token,
      },
      params: {
        where: `(keycloak_id,eq,${keycloakId})`,
        limit: 1,
      },
    });

    if (result.list && result.list.length > 0) {
      const member = normalizeFieldsFromNocoDB(result.list[0]);
      console.log('[nocodb.getMemberByKeycloakId] Found member:', member.member_id || member.Id);
      return member;
    }

    console.log('[nocodb.getMemberByKeycloakId] No member found with Keycloak ID:', keycloakId);
    return null;
  } catch (error: any) {
    console.error('[nocodb.getMemberByKeycloakId] Error searching for member:', error);
    // Return null instead of throwing to allow graceful handling
    return null;
  }
};

export const createMember = async (data: Partial<Member>): Promise<Member> => {
  console.log('[nocodb.createMember] Creating member with fields:', Object.keys(data));
  
  // Create a clean data object that matches the member schema
  const cleanData: Record<string, any> = {};

  // Only include fields that are part of the member schema
  const allowedFields = [
    "first_name",
    "last_name", 
    "email",
    "phone",
    "current_year_dues_paid",
    "nationality",
    "date_of_birth",
    "membership_date_paid",
    "payment_due_date",
    "membership_status",
    "address",
    "member_since",
    "member_id",
    "keycloak_id"
  ];

  // Filter the data to only include allowed fields
  for (const field of allowedFields) {
    if (field in data) {
      cleanData[field] = (data as any)[field];
    }
  }

  // Remove any computed or relation fields that shouldn't be sent
  delete cleanData.Id;
  delete cleanData.CreatedAt;
  delete cleanData.UpdatedAt;
  delete cleanData.FullName;
  delete cleanData.FormattedPhone;

  // Fix date formatting for PostgreSQL
  if (cleanData['date_of_birth']) {
    cleanData['date_of_birth'] = convertDateFormat(cleanData['date_of_birth']);
  }
  if (cleanData['membership_date_paid']) {
    cleanData['membership_date_paid'] = convertDateFormat(cleanData['membership_date_paid']);
  }
  if (cleanData['payment_due_date']) {
    cleanData['payment_due_date'] = convertDateFormat(cleanData['payment_due_date']);
  }

  console.log('[nocodb.createMember] Clean data fields:', Object.keys(cleanData));
  const url = createTableUrl(Table.Members);

  try {
    const result = await $fetch<Member>(url, {
      method: "POST",
      headers: {
        "xc-token": getNocoDbConfiguration().token,
      },
      body: cleanData,
    });
    console.log('[nocodb.createMember] Created member with ID:', result.Id);
    return result;
  } catch (error) {
    console.error('[nocodb.createMember] Create failed:', error);
    console.error('[nocodb.createMember] Error details:', error instanceof Error ? error.message : 'Unknown error');
    throw error;
  }
};

export const updateMember = async (id: string, data: Partial<Member>, retryCount = 0): Promise<Member> => {
  console.log('[nocodb.updateMember] Updating member:', id, 'Retry:', retryCount);
  console.log('[nocodb.updateMember] Data fields:', Object.keys(data));
  
  // First, try to verify the record exists
  if (retryCount === 0) {
    try {
      console.log('[nocodb.updateMember] Verifying record exists...');
      const existingRecord = await getMemberById(id);
      console.log('[nocodb.updateMember] Record exists with ID:', existingRecord.Id);
    } catch (verifyError: any) {
      console.error('[nocodb.updateMember] Failed to verify record:', verifyError);
      if (verifyError.statusCode === 404 || verifyError.status === 404) {
        console.error('[nocodb.updateMember] Record verification failed - record not found');
      }
    }
  }
  
  // Create a clean data object
  const cleanData: Record<string, any> = {};

  // Only include fields that are part of the member schema
  const allowedFields = [
    "first_name",
    "last_name", 
    "email",
    "phone",
    "current_year_dues_paid",
    "nationality",
    "date_of_birth",
    "membership_date_paid",
    "payment_due_date",
    "membership_status",
    "address",
    "member_since",
    "keycloak_id"
  ];

  // Filter the data to only include allowed fields
  for (const field of allowedFields) {
    if (field in data) {
      const value = (data as any)[field];
      
      // Handle clearing fields - NocoDB requires null for clearing, not undefined
      if (value === undefined) {
        cleanData[field] = null;
        console.log(`[nocodb.updateMember] Converting undefined to null for field: ${field}`);
      } else {
        cleanData[field] = value;
      }
    }
  }

  // Fix date formatting for PostgreSQL
  if (cleanData['date_of_birth']) {
    cleanData['date_of_birth'] = convertDateFormat(cleanData['date_of_birth']);
  }
  if (cleanData['membership_date_paid']) {
    cleanData['membership_date_paid'] = convertDateFormat(cleanData['membership_date_paid']);
  }
  if (cleanData['payment_due_date']) {
    cleanData['payment_due_date'] = convertDateFormat(cleanData['payment_due_date']);
  }

  console.log('[nocodb.updateMember] Clean data fields:', Object.keys(cleanData));
  
  // PATCH requires ID in the body (not in URL)
  cleanData.Id = parseInt(id);
  
  const url = createTableUrl(Table.Members);

  try {
    console.log('[nocodb.updateMember] Sending PATCH request');
    
    const result = await $fetch<Member>(url, {
      method: "PATCH",
      headers: {
        "xc-token": getNocoDbConfiguration().token,
        "Content-Type": "application/json"
      },
      body: cleanData
    });
    console.log('[nocodb.updateMember] Update successful for ID:', id);
    return result;
  } catch (error: any) {
    console.error('[nocodb.updateMember] Update failed:', error);
    console.error('[nocodb.updateMember] Error details:', error instanceof Error ? error.message : 'Unknown error');
    
    // If it's a 404 error and we haven't retried too many times, wait and retry
    if ((error.statusCode === 404 || error.status === 404) && retryCount < 3) {
      console.error('[nocodb.updateMember] 404 Error - Record not found. This might be a sync delay.');
      console.error(`Retrying in ${(retryCount + 1) * 1000}ms... (Attempt ${retryCount + 1}/3)`);
      
      // Wait with exponential backoff
      await new Promise(resolve => setTimeout(resolve, (retryCount + 1) * 1000));
      
      // Retry the update
      return updateMember(id, data, retryCount + 1);
    }
    
    throw error;
  }
};

export const deleteMember = async (id: string) => {
  const startTime = Date.now();
  console.log('[nocodb.deleteMember] =========================');
  console.log('[nocodb.deleteMember] DELETE operation started at:', new Date().toISOString());
  console.log('[nocodb.deleteMember] Target ID:', id);
  
  const url = createTableUrl(Table.Members);
  console.log('[nocodb.deleteMember] URL:', url);
  
  const requestBody = {
    "Id": parseInt(id)
  };
  
  console.log('[nocodb.deleteMember] Request configuration:');
  console.log('  Method: DELETE');
  console.log('  URL:', url);
  console.log('  Body:', JSON.stringify(requestBody, null, 2));
  
  try {
    const result = await $fetch(url, {
      method: "DELETE",
      headers: {
        "xc-token": getNocoDbConfiguration().token,
        "Content-Type": "application/json"
      },
      body: requestBody
    });
    
    console.log('[nocodb.deleteMember] DELETE successful');
    console.log('[nocodb.deleteMember] Duration:', Date.now() - startTime, 'ms');
    
    return result;
  } catch (error: any) {
    console.error('[nocodb.deleteMember] DELETE FAILED');
    console.error('[nocodb.deleteMember] Error type:', error.constructor.name);
    console.error('[nocodb.deleteMember] Error message:', error.message);
    console.error('[nocodb.deleteMember] Duration:', Date.now() - startTime, 'ms');
    throw error;
  }
};

// Centralized error handling
export const handleNocoDbError = (error: any, operation: string, entityType: string = 'Member') => {
  console.error(`[nocodb.${operation}] =========================`);
  console.error(`[nocodb.${operation}] ERROR in ${operation} for ${entityType}`);
  console.error(`[nocodb.${operation}] Error type:`, error.constructor?.name || 'Unknown');
  console.error(`[nocodb.${operation}] Error status:`, error.statusCode || error.status || 'Unknown');
  console.error(`[nocodb.${operation}] Error message:`, error.message || 'Unknown error');
  console.error(`[nocodb.${operation}] Error data:`, error.data);
  console.error(`[nocodb.${operation}] =========================`);
  
  // Provide more specific error messages
  if (error.statusCode === 401 || error.status === 401) {
    throw createError({
      statusCode: 401,
      statusMessage: `Authentication failed when accessing ${entityType}. Please check your access permissions.`
    });
  } else if (error.statusCode === 403 || error.status === 403) {
    throw createError({
      statusCode: 403,
      statusMessage: `Access denied to ${entityType}. This feature requires appropriate privileges.`
    });
  } else if (error.statusCode === 404 || error.status === 404) {
    throw createError({
      statusCode: 404,
      statusMessage: `${entityType} not found. Please verify the record exists.`
    });
  } else if (error.code === 'NETWORK_ERROR' || error.code === 'TIMEOUT') {
    throw createError({
      statusCode: 503,
      statusMessage: `${entityType} database is temporarily unavailable. Please try again in a moment.`
    });
  }
  
  throw error;
};
