import { defineStore } from 'pinia'
import { tableDataStorage } from '~/utils/storage'

export interface TableDataState<T = Record<string, unknown>> {
  data: T[]
  loading: boolean
  error: string | null
  lastUpdated: string | null
  initialized: boolean
  limit: number
}

export interface TableDataGetters<T = Record<string, unknown>> {
  allRecords: (state: TableDataState<T>) => T[]
  hasData: (state: TableDataState<T>) => boolean
  isLoading: (state: TableDataState<T>) => boolean
  hasError: (state: TableDataState<T>) => boolean
  recordCount: (state: TableDataState<T>) => number
  formattedLastUpdated: (state: TableDataState<T>) => string
  isStale: (state: TableDataState<T>) => boolean
}

export interface TableDataActions<T = Record<string, unknown>> {
  loadData(force?: boolean): Promise<void>
  refreshData(): Promise<void>
  fetchData(): Promise<void>
  clearData(): Promise<void>
  loadFromCache(): Promise<void>
  extractErrorMessage(error: unknown): string
  initialize(): Promise<void>
  getRecord(id: string | number): T | undefined
  filterRecords(predicate: (record: T) => boolean): T[]
  loadAllDataInBatches(onProgress?: (progress: number) => void): Promise<void>
  loadLatestDataInBatches(onProgress?: (progress: number) => void): Promise<void>
}

/**
 * Factory function to create a Pinia store for table data
 * @param tableName - The name of the table
 * @param defaultLimit - Default limit for data fetching (default: 100)
 */
export function createTableDataStore<T = Record<string, unknown>>(
  tableName: string,
  defaultLimit: number = 100
) {
  const storeId = `table-${tableName}`
  const cacheKey = `table-data-${tableName}`

  return defineStore(storeId, {
    state: (): TableDataState<T> => ({
      data: [],
      loading: false,
      error: null,
      lastUpdated: null,
      initialized: false,
      limit: defaultLimit
    }),

    getters: {
      /**
       * Get all records
       */
      allRecords(): T[] {
        return this.data as T[]
      },

      /**
       * Check if data is available
       */
      hasData(): boolean {
        return this.data.length > 0
      },

      /**
       * Check if data is currently loading
       */
      isLoading(): boolean {
        return this.loading
      },

      /**
       * Check if there's an error
       */
      hasError(): boolean {
        return !!this.error
      },

      /**
       * Get total number of records
       */
      recordCount(): number {
        return this.data.length
      },

      /**
       * Get formatted last updated time
       */
      formattedLastUpdated(): string {
        if (!this.lastUpdated) return 'Nunca'

        try {
          return new Date(this.lastUpdated).toLocaleString('es-ES', {
            year: 'numeric',
            month: 'long',
            day: 'numeric',
            hour: '2-digit',
            minute: '2-digit'
          })
        } catch {
          return 'Fecha inválida'
        }
      },

      /**
       * Check if data is stale (older than 5 minutes)
       */
      isStale(): boolean {
        if (!this.lastUpdated) return true

        const lastUpdate = new Date(this.lastUpdated)
        const now = new Date()
        const fiveMinutes = 5 * 60 * 1000

        return (now.getTime() - lastUpdate.getTime()) > fiveMinutes
      }
    },

    actions: {
      /**
       * Load data lazily (only if not already loaded)
       */
      async loadData(force = false): Promise<void> {
        // Don't load if already loading
        if (this.loading) return

        // Don't load if already initialized and not forced
        if (this.initialized && !force && !this.isStale) return

        await this.fetchData()
      },

      /**
       * Force refresh data
       */
      async refreshData(): Promise<void> {
        await this.fetchData()
      },

      /**
       * Internal method to fetch data from API
       */
      async fetchData(): Promise<void> {
        this.loading = true
        this.error = null

        try {
          const response = await $fetch(`/api/data/${tableName}`, {
            query: { limit: String(this.limit) }
          })

          if (response && typeof response === 'object' && 'records' in response) {
            const dataset = response as { records?: any[] }
            this.data = Array.isArray(dataset.records) ? dataset.records : []
          } else if (Array.isArray(response)) {
            this.data = response
          } else {
            this.data = []
          }

          this.lastUpdated = new Date().toISOString()
          this.initialized = true

          // Persist to IndexedDB for offline access
          if (process.client) {
            try {
              await tableDataStorage.setItem(cacheKey, {
                data: this.data,
                lastUpdated: this.lastUpdated,
                limit: this.limit
              })
            } catch (error) {
              console.warn(`Failed to persist ${tableName} data to IndexedDB:`, error)
            }
          }
        } catch (error: any) {
          this.error = this.extractErrorMessage(error)
          console.error(`Error fetching ${tableName} data:`, error)

          // Try to load from cache if available
          if (process.client && !this.hasData) {
            this.loadFromCache()
          }
        } finally {
          this.loading = false
        }
      },

      /**
       * Load data from IndexedDB cache
       */
      async loadFromCache(): Promise<void> {
        if (!process.client) {
          console.log(`[${tableName}] loadFromCache: Not on client, skipping`)
          return
        }

        try {
          console.log(`[${tableName}] loadFromCache: Attempting to load from key: ${cacheKey}`)
          const parsedCache = await tableDataStorage.getItem(cacheKey)
          if (parsedCache) {
            console.log(`[${tableName}] loadFromCache: Found ${parsedCache.data?.length || 0} records in cache`)
            this.data = parsedCache.data || []
            this.lastUpdated = parsedCache.lastUpdated || null
            this.limit = parsedCache.limit || defaultLimit
            this.initialized = true
            console.log(`[${tableName}] loadFromCache: Successfully loaded, data.length: ${this.data.length}`)
          } else {
            console.log(`[${tableName}] loadFromCache: No cached data found`)
          }
        } catch (error) {
          console.error(`[${tableName}] loadFromCache: Failed to load data from cache:`, error)
        }
      },

      /**
       * Clear all data
       */
      async clearData(): Promise<void> {
        this.data = []
        this.error = null
        this.lastUpdated = null
        this.initialized = false

        if (process.client) {
          try {
            await tableDataStorage.removeItem(cacheKey)
          } catch (error) {
            console.warn(`Failed to clear ${tableName} data cache:`, error)
          }
        }
      },

      /**
       * Extract error message from various error types
       */
      extractErrorMessage(error: unknown): string {
        if (error && typeof error === 'object' && 'statusMessage' in error) {
          return String((error as { statusMessage: string }).statusMessage)
        }

        if (error instanceof Error) {
          return error.message
        }

        return `Error inesperado al cargar datos de ${tableName}`
      },

      /**
       * Initialize store (called on app startup)
       */
      async initialize(): Promise<void> {
        // Load from cache first for immediate availability
        await this.loadFromCache()

        // Then try to fetch fresh data
        await this.loadData()
      },

      /**
       * Get a specific record by ID
       */
      getRecord(id: string | number): T | undefined {
        return this.data.find((record: any) => {
          return record.id === id || String(record.id) === String(id)
        })
      },

      /**
       * Filter records by a predicate function
       */
      filterRecords(predicate: (record: T) => boolean): T[] {
        return (this.data as T[]).filter(predicate)
      },

      /**
       * Load all data in batches with progress callback
       */
      async loadAllDataInBatches(onProgress?: (progress: number) => void): Promise<void> {
        console.log(`[${tableName}] loadAllDataInBatches: Starting...`)
        this.loading = true
        this.error = null

        try {
          // Clear existing data
          console.log(`[${tableName}] loadAllDataInBatches: Clearing ${this.data.length} existing records`)
          this.data.splice(0, this.data.length)
          console.log(`[${tableName}] loadAllDataInBatches: Data cleared, length now: ${this.data.length}`)

          let offset = 0
          const limit = 500
          let hasMore = true
          let totalFetched = 0
          let estimatedTotal = 0

          while (hasMore) {
            console.log(`[${tableName}] loadAllDataInBatches: Fetching batch at offset ${offset}`)
            const response = await $fetch(`/api/data/${tableName}`, {
              params: { limit, offset }
            })

            if (!response) {
              console.log(`[${tableName}] loadAllDataInBatches: No response, stopping`)
              hasMore = false
              break
            }

            // Get total count from first response
            if (offset === 0 && response.count) {
              estimatedTotal = response.count
              console.log(`[${tableName}] loadAllDataInBatches: Estimated total: ${estimatedTotal}`)
            }

            if (response.records && response.records.length > 0) {
              console.log(`[${tableName}] loadAllDataInBatches: Got ${response.records.length} records`)
              this.data.push(...response.records)
              totalFetched += response.records.length
              console.log(`[${tableName}] loadAllDataInBatches: Total fetched so far: ${totalFetched}, data.length: ${this.data.length}`)

              // Update progress
              if (onProgress) {
                if (estimatedTotal > 0) {
                  onProgress(Math.min(95, (totalFetched / estimatedTotal) * 100))
                } else {
                  onProgress(Math.min(95, (totalFetched / 10000) * 100))
                }
              }

              // Check if we got fewer records than requested (means we're at the end)
              hasMore = response.records.length === limit
              offset += limit
            } else {
              console.log(`[${tableName}] loadAllDataInBatches: No records in response, stopping`)
              hasMore = false
            }
          }

          this.lastUpdated = new Date().toISOString()
          this.initialized = true

          console.log(`[${tableName}] loadAllDataInBatches: Finished loading ${this.data.length} records`)

          // Persist to IndexedDB
          if (process.client) {
            try {
              console.log(`[${tableName}] loadAllDataInBatches: Persisting to IndexedDB with key: ${cacheKey}`)
              const dataToSave = {
                data: this.data,
                lastUpdated: this.lastUpdated,
                limit: this.limit
              }
              console.log(`[${tableName}] loadAllDataInBatches: Saving ${dataToSave.data.length} records`)
              await tableDataStorage.setItem(cacheKey, dataToSave)
              console.log(`[${tableName}] loadAllDataInBatches: Successfully saved to IndexedDB`)

              // Verify it was saved
              const saved = await tableDataStorage.getItem(cacheKey)
              if (saved) {
                console.log(`[${tableName}] loadAllDataInBatches: Verification - IndexedDB contains ${saved.data?.length || 0} records`)
              }
            } catch (error) {
              console.error(`[${tableName}] loadAllDataInBatches: Failed to persist data to IndexedDB:`, error)
            }
          } else {
            console.log(`[${tableName}] loadAllDataInBatches: Not on client, skipping IndexedDB`)
          }

          if (onProgress) {
            onProgress(100)
          }
        } catch (error: any) {
          this.error = this.extractErrorMessage(error)
          console.error(`[${tableName}] loadAllDataInBatches: Error:`, error)
          throw error
        } finally {
          this.loading = false
          console.log(`[${tableName}] loadAllDataInBatches: Completed`)
        }
      },

      /**
       * Load only latest data (incremental) in batches with progress callback
       */
      async loadLatestDataInBatches(onProgress?: (progress: number) => void): Promise<void> {
        console.log(`[${tableName}] loadLatestDataInBatches: Starting...`)
        this.loading = true
        this.error = null

        try {
          // Find the most recent created_at in memory
          let lastCreatedAt: string | null = null
          if (this.data.length > 0) {
            const sortedRecords = [...(this.data as any[])].sort((a, b) => {
              const dateA = new Date(a.created_at || 0).getTime()
              const dateB = new Date(b.created_at || 0).getTime()
              return dateB - dateA
            })
            lastCreatedAt = sortedRecords[0]?.created_at || null
            console.log(`[${tableName}] loadLatestDataInBatches: Most recent created_at in memory: ${lastCreatedAt}`)
          } else {
            console.log(`[${tableName}] loadLatestDataInBatches: No data in memory, will fetch all`)
          }

          let offset = 0
          const limit = 500
          let hasMore = true
          let newRecordsCount = 0

          while (hasMore) {
            console.log(`[${tableName}] loadLatestDataInBatches: Fetching batch at offset ${offset}`)
            const response = await $fetch(`/api/data/${tableName}`, {
              params: {
                limit,
                offset,
                orderBy: 'created_at',
                orderDirection: 'desc'
              }
            })

            if (!response || !response.records || response.records.length === 0) {
              console.log(`[${tableName}] loadLatestDataInBatches: No response or records, stopping`)
              hasMore = false
              break
            }

            console.log(`[${tableName}] loadLatestDataInBatches: Got ${response.records.length} records`)

            // Filter only records newer than what we have
            const newRecords = lastCreatedAt
              ? response.records.filter((r: any) => r.created_at > lastCreatedAt)
              : response.records

            console.log(`[${tableName}] loadLatestDataInBatches: ${newRecords.length} new records after filtering`)

            if (newRecords.length > 0) {
              this.data.unshift(...newRecords)
              newRecordsCount += newRecords.length
              console.log(`[${tableName}] loadLatestDataInBatches: Total new records: ${newRecordsCount}, data.length: ${this.data.length}`)
            }

            // Update progress
            if (onProgress) {
              onProgress(Math.min(95, (newRecordsCount / 500) * 100))
            }

            // Stop if we found records older than our last one
            if (lastCreatedAt && response.records.some((r: any) => r.created_at <= lastCreatedAt)) {
              console.log(`[${tableName}] loadLatestDataInBatches: Found older records, stopping`)
              hasMore = false
            } else {
              hasMore = response.records.length === limit
              offset += limit
            }
          }

          this.lastUpdated = new Date().toISOString()
          console.log(`[${tableName}] loadLatestDataInBatches: Added ${newRecordsCount} new records, total: ${this.data.length}`)

          // Persist to IndexedDB
          if (process.client) {
            try {
              console.log(`[${tableName}] loadLatestDataInBatches: Persisting to IndexedDB with key: ${cacheKey}`)
              const dataToSave = {
                data: this.data,
                lastUpdated: this.lastUpdated,
                limit: this.limit
              }
              console.log(`[${tableName}] loadLatestDataInBatches: Saving ${dataToSave.data.length} records`)
              await tableDataStorage.setItem(cacheKey, dataToSave)
              console.log(`[${tableName}] loadLatestDataInBatches: Successfully saved to IndexedDB`)

              // Verify it was saved
              const saved = await tableDataStorage.getItem(cacheKey)
              if (saved) {
                console.log(`[${tableName}] loadLatestDataInBatches: Verification - IndexedDB contains ${saved.data?.length || 0} records`)
              }
            } catch (error) {
              console.error(`[${tableName}] loadLatestDataInBatches: Failed to persist data to IndexedDB:`, error)
            }
          } else {
            console.log(`[${tableName}] loadLatestDataInBatches: Not on client, skipping IndexedDB`)
          }

          if (onProgress) {
            onProgress(100)
          }
        } catch (error: any) {
          this.error = this.extractErrorMessage(error)
          console.error(`[${tableName}] loadLatestDataInBatches: Error:`, error)
          throw error
        } finally {
          this.loading = false
          console.log(`[${tableName}] loadLatestDataInBatches: Completed`)
        }
      }
    }
  })
}

/**
 * Helper to get or create a table data store
 */
export function useTableDataStore<T = Record<string, unknown>>(
  tableName: string,
  limit?: number
) {
  const store = createTableDataStore<T>(tableName, limit)
  return store()
}