/**
 * WorldMind API Utilities
 *
 * This file provides the core API interaction functionality for the WorldMind application.
 *
 * ## Architecture Overview
 *
 * The services layer is organized as follows:
 *
 * - `services/utils.ts` (this file): Core API utilities that handle authentication,
 *   request formatting, and common CRUD operations.
 *
 * - `services/[table]/query.ts`: Table-specific API methods that use the core utilities
 *   to interact with specific API endpoints. Each table has its own query file with
 *   specialized methods for that data type (e.g., courses, users, etc.).
 *
 * - `services/[table]/mock.ts`: Mock implementations of the same methods found in query.ts.
 *   These files serve as drop-in replacements for development or testing purposes.
 *
 * ## Mock Implementation
 *
 * The mock.ts files implement the EXACT SAME interface as their query.ts counterparts.
 * This means:
 *
 * - All method signatures are identical (same names, parameters, and return types)
 * - All exported types and interfaces are identical
 * - The mock implementations return realistic data structures that match what the API would return
 *
 * This allows for easy switching between real API calls and mock data by simply changing imports.
 *
 * ## Usage
 *
 * For production:
 * \`\`\`typescript
 * import { getCourses } from '@/services/courses/query';
 * \`\`\`
 *
 * For development/testing with mock data:
 * \`\`\`typescript
 * import { getCourses } from '@/services/courses/mock';
 * \`\`\`
 *
 * The application can be configured to use mock data by setting the environment variable:
 * NEXT_PUBLIC_USE_REMOTE_API=false
 *
 * ## API Base URL
 *
 * The API base URL is determined from environment variables:
 * - NEXT_PUBLIC_API_BASE_URL (if set)
 * - Defaults to "https://worldmind-api.nishamengmingli.com/api"
 */

// Get the API base URL from environment variables
const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || "https://worldmind-api.nishamengmingli.com/api"

/**
 * Get the full API URL for a given endpoint
 * @param endpoint The API endpoint path
 * @returns The full API URL
 */
export const getApiUrl = (endpoint: string): string => {
  const baseUrl = process.env.NEXT_PUBLIC_API_BASE_URL || "https://worldmind-api.nishamengmingli.com/api"
  // Ensure endpoint starts with a slash
  const formattedEndpoint = endpoint.startsWith("/") ? endpoint : `/${endpoint}`
  return `${baseUrl}${formattedEndpoint}`
}

/**
 * Get the authentication token from localStorage
 */
const getAuthToken = (): string | null => {
  if (typeof window === "undefined") {
    return null
  }

  try {
    // Updated to use the correct localStorage key "login_info" as used in services/users/login.ts
    const loginInfo = localStorage.getItem("login_info")
    if (!loginInfo) {
      return null
    }

    const parsedInfo = JSON.parse(loginInfo)
    const token = parsedInfo?.token
    return token || null
  } catch (error) {
    console.error("Error retrieving auth token:", error)
    return null
  }
}

/**
 * Create headers for API requests
 */
const createHeaders = (includeAuth = true): HeadersInit => {
  const headers: HeadersInit = {
    "Content-Type": "application/json",
    Accept: "application/json",
  }

  // Always check for token and include it if present, regardless of includeAuth parameter
  const token = getAuthToken()
  if (token) {
    headers["Authorization"] = `Bearer ${token}`
  }

  return headers
}

/**
 * Store API response metadata in localStorage
 */
const storeResponseMetadata = (table: string, responseData: any) => {
  if (typeof window === "undefined") return

  try {
    // Create a copy of the response without the data array to save space
    const metadataCopy = { ...responseData }

    // Remove the data array from the metadata
    if (metadataCopy.data) {
      delete metadataCopy.data
    }

    localStorage.setItem(`${table}_query_response_meta`, JSON.stringify(metadataCopy))
  } catch (error) {
    console.error(`Failed to store metadata for ${table}:`, error)
  }
}

/**
 * Get API response metadata from localStorage
 */
export const getResponseMetadata = (table: string) => {
  if (typeof window === "undefined") return null

  try {
    const metadata = localStorage.getItem(`${table}_query_response_meta`)
    return metadata ? JSON.parse(metadata) : null
  } catch (error) {
    console.error(`Failed to retrieve metadata for ${table}:`, error)
    return null
  }
}

/**
 * Extract summary information from data items
 * @param data Array of data items
 * @returns Brief summary with count and list of id, name/title
 */
const extractDataSummary = (data: any[]): string => {
  if (!Array.isArray(data) || data.length === 0) {
    return "0 items"
  }

  const count = data.length
  const summaries = data
    .map((item) => {
      const id = item.id || "no-id"
      const name = item.name || item.title || item.email || "unnamed"
      return `{id:${id}, ${name}}`
    })
    .join(", ")

  return `${count} items: ${summaries}`
}

/**
 * Handle API response
 */
const handleResponse = async (response: Response) => {
  const url = response.url.split("?")[0] // Remove query params for cleaner logs

  if (!response.ok) {
    // Try to get error details from response
    let errorMessage = `API Error: ${response.status} ${response.statusText}`
    try {
      const errorData = await response.json()
      if (errorData.message) {
        errorMessage = errorData.message
      }
    } catch (e) {
      // If we can't parse the error, just use the status
    }

    throw new Error(errorMessage)
  }

  const data = await response.json()

  // Log a summary of the response data
  if (data && typeof data === "object") {
    if (Array.isArray(data)) {
      console.log(`API Response [${response.status}] from ${url}: ${extractDataSummary(data)}`)
    } else if (data.data && Array.isArray(data.data)) {
      console.log(`API Response [${response.status}] from ${url}: ${extractDataSummary(data.data)}`)
    } else {
      const id = data.id || "no-id"
      const name = data.name || data.title || data.email || "unnamed"
      console.log(`API Response [${response.status}] from ${url}: {id:${id}, ${name}}`)
    }
  }

  return data
}

/**
 * Query data from the API
 * @param table The table/endpoint to query
 * @param params Optional query parameters
 * @param returnFullResponse Whether to return the full response with metadata (default: false)
 * @returns Promise with the query results
 */
export const query = async (table: string, params?: Record<string, any>, returnFullResponse = false) => {
  // Build query string from params
  let queryString = ""
  if (params && Object.keys(params).length > 0) {
    queryString =
      "?" +
      new URLSearchParams(
        Object.entries(params).reduce(
          (acc, [key, value]) => {
            if (value !== undefined && value !== null) {
              acc[key] = String(value)
            }
            return acc
          },
          {} as Record<string, string>,
        ),
      ).toString()
  }

  const url = `${API_BASE_URL}/services/${table}${queryString}`
  console.log(`API Request [GET]: ${table}${queryString ? " with params" : ""}`)
  console.log(`Complete URL: ${url}`)

  // Debug auth token
  const token = getAuthToken()
  console.log(`Auth token exists: ${!!token}`)
  if (token) {
    console.log(`Auth token first 10 chars: ${token.substring(0, 10)}...`)
  }

  try {
    const headers = createHeaders()
    console.log(`Request headers:`, headers)

    const response = await fetch(url, {
      method: "GET",
      headers: headers,
      credentials: "omit",
    })

    console.log(`Response status:`, response.status, response.statusText)

    const responseData = await handleResponse(response)

    // Check if the response has a data property (paginated response)
    if (responseData && typeof responseData === "object" && "data" in responseData) {
      // Store metadata in localStorage
      storeResponseMetadata(table, responseData)

      // Return either just the data array or the full response
      return returnFullResponse ? responseData : responseData.data
    }

    // If response doesn't have a data property, return as is
    return responseData
  } catch (error) {
    console.error(`Failed to query ${table}: ${error instanceof Error ? error.message : String(error)}`)
    throw new Error(`Failed to query ${table}: ${error instanceof Error ? error.message : String(error)}`)
  }
}

/**
 * Query data from the API with a full path
 * @param api_path The full API path (without base URL)
 * @param method The HTTP method to use
 * @param data Optional data to send with the request
 * @returns Promise with the query results
 */
export const queryFull = async (api_path: string, method = "GET", data?: any) => {
  const url = `${API_BASE_URL}/${api_path}`
  console.log(`API Request [${method}]: ${api_path}`)
  console.log(`Complete URL: ${url}`)

  // Debug auth token
  const token = getAuthToken()
  console.log(`Auth token exists: ${!!token}`)
  if (token) {
    console.log(`Auth token first 10 chars: ${token.substring(0, 10)}...`)
  }

  try {
    const headers = createHeaders()
    console.log(`Request headers:`, headers)

    const options: RequestInit = {
      method,
      headers,
      credentials: "omit",
    }

    // Add body for POST, PUT, PATCH methods
    if (["POST", "PUT", "PATCH"].includes(method) && data) {
      options.body = JSON.stringify(data)
    }

    const response = await fetch(url, options)
    console.log(`Response status:`, response.status, response.statusText)

    return handleResponse(response)
  } catch (error) {
    console.error(`Failed to query ${api_path}: ${error instanceof Error ? error.message : String(error)}`)
    throw new Error(`Failed to query ${api_path}: ${error instanceof Error ? error.message : String(error)}`)
  }
}

/**
 * Generic request method
 * @param path The API path
 * @param method GET|PUT|POST
 * @param data Array object with relations
 * @returns
 */
export const request = async (path: string, method: string, data: any) => {
  const url = `${API_BASE_URL}/${path}`
  console.log(`API Request [${method}]: ${path}`)

  try {
    const options: RequestInit = {
      method: method,
      headers: createHeaders(),
      credentials: "omit",
    }

    // Only add body for POST, PUT, PATCH methods
    if (["POST", "PUT", "PATCH"].includes(method) && data) {
      options.body = JSON.stringify(data)
    }

    const response = await fetch(url, options)

    return handleResponse(response)
  } catch (error) {
    console.error(`Failed to ${method} ${path}: ${error instanceof Error ? error.message : String(error)}`)
    throw new Error(`Failed to ${method} ${path}: ${error instanceof Error ? error.message : String(error)}`)
  }
}

/**
 * Create a new record
 * @param table The table/endpoint where the record should be created
 * @param data The data for the new record
 * @returns Promise with the created record
 */
export const create = async (table: string, data: any) => {
  const url = `${API_BASE_URL}/services/${table}`
  console.log(`API Request [POST]: ${table}`)

  try {
    const response = await fetch(url, {
      method: "POST",
      headers: createHeaders(),
      body: JSON.stringify(data),
      credentials: "omit",
    })

    return handleResponse(response)
  } catch (error) {
    console.error(`Failed to create record in ${table}: ${error instanceof Error ? error.message : String(error)}`)
    throw new Error(`Failed to create record in ${table}: ${error instanceof Error ? error.message : String(error)}`)
  }
}

/**
 * Update an existing record
 * @param table The table/endpoint where the record should be updated
 * @param id The ID of the record to update
 * @param data The updated data
 * @returns Promise with the updated record
 */
export const update = async (table: string, id: number | string, data: any) => {
  const url = `${API_BASE_URL}/services/${table}/${id}`
  console.log(`API Request [PUT]: ${table}/${id}`)

  try {
    const response = await fetch(url, {
      method: "PUT", // Some APIs use PATCH instead
      headers: createHeaders(),
      body: JSON.stringify(data),
      credentials: "omit",
    })

    return handleResponse(response)
  } catch (error) {
    console.error(`Failed to update record in ${table}: ${error instanceof Error ? error.message : String(error)}`)
    throw new Error(`Failed to update record in ${table}: ${error instanceof Error ? error.message : String(error)}`)
  }
}

/**
 * Delete a record
 * @param table The table/endpoint where the record should be deleted
 * @param id The ID of the record to delete
 * @returns Promise with the deletion result
 */
export const remove = async (table: string, id: number | string) => {
  const url = `${API_BASE_URL}/services/${table}/${id}`
  console.log(`API Request [DELETE]: ${table}/${id}`)

  try {
    const response = await fetch(url, {
      method: "DELETE",
      headers: createHeaders(),
      credentials: "omit",
    })

    return handleResponse(response)
  } catch (error) {
    console.error(`Failed to delete record in ${table}: ${error instanceof Error ? error.message : String(error)}`)
    throw new Error(`Failed to delete record in ${table}: ${error instanceof Error ? error.message : String(error)}`)
  }
}

/**
 * Custom API call for more complex operations
 * @param endpoint The API endpoint
 * @param options Fetch options
 * @returns Promise with the API response
 */
export const apiCall = async (endpoint: string, options: RequestInit = {}) => {
  const url = endpoint.startsWith("http")
    ? endpoint
    : `${API_BASE_URL}/${endpoint.startsWith("/") ? endpoint.slice(1) : endpoint}`

  console.log(`API Request [${options.method || "GET"}]: ${endpoint.split("?")[0]}`)

  const defaultOptions: RequestInit = {
    headers: createHeaders(),
    credentials: "omit",
  }

  try {
    const response = await fetch(url, { ...defaultOptions, ...options })
    return handleResponse(response)
  } catch (error) {
    console.error(`API call failed: ${error instanceof Error ? error.message : String(error)}`)
    throw new Error(`API call failed: ${error instanceof Error ? error.message : String(error)}`)
  }
}

/**
 * Login to the API
 * @param email User email
 * @param password User password
 * @returns Promise with the login result
 */
export const login = async (email: string, password: string) => {
  const url = `${API_BASE_URL}/auth/login`
  console.log(`API Request [POST]: auth/login`)

  try {
    const response = await fetch(url, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Accept: "application/json",
      },
      body: JSON.stringify({ email, password }),
      credentials: "omit",
    })

    return handleResponse(response)
  } catch (error) {
    console.error(`Login failed: ${error instanceof Error ? error.message : String(error)}`)
    throw new Error(`Login failed: ${error instanceof Error ? error.message : String(error)}`)
  }
}

/**
 * Logout from the API
 * @returns Promise with the logout result
 */
export const logout = async () => {
  const token = getAuthToken()
  if (!token) return { success: true }

  const url = `${API_BASE_URL}/auth/logout`
  console.log(`API Request [POST]: auth/logout`)

  try {
    const response = await fetch(url, {
      method: "POST",
      headers: createHeaders(),
      credentials: "omit",
    })

    // Clear local storage regardless of API response
    if (typeof window !== "undefined") {
      localStorage.removeItem("login_info") // Updated to use the correct key
    }

    return handleResponse(response)
  } catch (error) {
    // Still clear local storage even if API call fails
    if (typeof window !== "undefined") {
      localStorage.removeItem("login_info") // Updated to use the correct key
    }

    console.error(`Logout failed: ${error instanceof Error ? error.message : String(error)}`)
    throw new Error(`Logout failed: ${error instanceof Error ? error.message : String(error)}`)
  }
}

/**
 * Convert order item to HTML string based on item type
 * @param item OrderItem with purchasable details
 * @returns HTML string representation
 */
export const orderItemToString = (item: any): string => {
  if (!item.purchasable) {
    return `<span>${item.item_type}</span>`
  }

  const { name, title, description } = item.purchasable

  if (item.item_type === "App\\Models\\Course") {
    return `<h4>${title || "Course"}</h4><p>${description || ""}</p>`
  }

  if (item.item_type === "App\\Models\\CoursePackage") {
    return `<h5>${name || "Package"}</h5><p>${description || ""}</p>`
  }

  if (item.item_type === "App\\Models\\CoursePackageClass") {
    return `<h6>${title || "Class"}</h6><p>${description || ""}</p>`
  }

  // Default fallback
  return `<span>${name || title || item.item_type}</span>`
}

export const formatCurrency = (value: any): string => {
  if (value === undefined || value === null) return "0.00";

  const numValue = typeof value === "number" ? value : Number.parseFloat(value);
  if (isNaN(numValue)) return "0.00";

  return numValue.toLocaleString("en-US", {
    minimumFractionDigits: 2,
    maximumFractionDigits: 2,
  });
};

export const parseArray = (values: string | string[]): string[] => {
  if (Array.isArray(values)) {
    return values;
  }

  try {
    const cleaned = values.replace(/\\"/g, '"');
    const parsed = JSON.parse(cleaned);
    if (Array.isArray(parsed)) {
      return parsed;
    }
  } catch (e) {
    console.error('Failed to parse value:', e);
  }

  // fallback: return empty array or original string in an array
  return [];
}

export const truncateString = (str: string, maxLength: number): string => {
  if (str.length <= maxLength) return str;
  return str.slice(0, maxLength) + '...';
};

export const toSqlDate = (date: Date): string => date.toISOString().slice(0, 19).replace('T', ' ');

export default {
  parseArray,
  toSqlDate,
  request,
  query,
  queryFull,
  create,
  update,
  remove,
  apiCall,
  login,
  logout,
  getResponseMetadata,
  orderItemToString,
  formatCurrency
}