import { useMemo } from 'react'

import { useEdgeFunctionsDiff, type EdgeFunctionsDiffResult } from './useEdgeFunctionsDiff'
import { useIsPgDeltaDiffEnabled } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
import { useBranchDiffQuery } from '@/data/branches/branch-diff-query'
import { useMigrationsQuery } from '@/data/database/migrations-query'

interface UseBranchMergeDiffProps {
  currentBranchRef?: string
  parentProjectRef?: string
  currentBranchConnectionString?: string
  parentBranchConnectionString?: string
  currentBranchCreatedAt?: string
}

export interface BranchMergeDiffResult {
  // Database diff
  diffContent: string | undefined
  isDatabaseDiffLoading: boolean
  isDatabaseDiffRefetching: boolean
  databaseDiffError: any
  refetchDatabaseDiff: () => void

  // Edge functions diff
  edgeFunctionsDiff: EdgeFunctionsDiffResult

  // Migrations
  currentBranchMigrations: any[] | undefined
  mainBranchMigrations: any[] | undefined
  refetchCurrentBranchMigrations: () => void
  refetchMainBranchMigrations: () => void

  // Branch state
  isBranchOutOfDateMigrations: boolean
  hasEdgeFunctionModifications: boolean
  missingFunctionsCount: number
  hasMissingFunctions: boolean
  outOfDateFunctionsCount: number
  hasOutOfDateFunctions: boolean
  isBranchOutOfDateOverall: boolean
  missingMigrationsCount: number
  modifiedFunctionsCount: number

  // Combined states
  isLoading: boolean
  hasChanges: boolean
}

export const useBranchMergeDiff = ({
  currentBranchRef,
  parentProjectRef,
  currentBranchConnectionString,
  parentBranchConnectionString,
  currentBranchCreatedAt,
}: UseBranchMergeDiffProps): BranchMergeDiffResult => {
  const pgDeltaDiffEnabled = useIsPgDeltaDiffEnabled()

  // Get database diff
  const {
    data: diffContent,
    isPending: isDatabaseDiffLoading,
    isRefetching: isDatabaseDiffRefetching,
    error: databaseDiffError,
    refetch: refetchDatabaseDiff,
  } = useBranchDiffQuery(
    {
      branchRef: currentBranchRef || '',
      projectRef: parentProjectRef || '',
      pgdelta: pgDeltaDiffEnabled,
    },
    {
      enabled: !!currentBranchRef && !!parentProjectRef,
      refetchOnMount: 'always',
      refetchOnWindowFocus: false,
      staleTime: 0,
    }
  )

  // Get migrations for both current branch and main branch
  const { data: currentBranchMigrations, refetch: refetchCurrentBranchMigrations } =
    useMigrationsQuery(
      {
        projectRef: currentBranchRef,
        connectionString: currentBranchConnectionString,
      },
      {
        enabled: !!currentBranchRef,
        staleTime: 3000,
      }
    )

  const { data: mainBranchMigrations, refetch: refetchMainBranchMigrations } = useMigrationsQuery(
    {
      projectRef: parentProjectRef,
      connectionString: parentBranchConnectionString,
    },
    {
      enabled: !!parentProjectRef,
      staleTime: 3000,
    }
  )

  // Get edge functions diff
  const edgeFunctionsDiff = useEdgeFunctionsDiff({
    currentBranchRef,
    mainBranchRef: parentProjectRef,
  })

  // Check if current branch is out of date with main branch (migrations)
  const isBranchOutOfDateMigrations = useMemo(() => {
    if (!currentBranchMigrations || !mainBranchMigrations) return false

    // Get the latest migration version from main branch
    const latestMainMigration = mainBranchMigrations[0] // migrations are ordered by version desc
    if (!latestMainMigration) return false

    // Check if current branch has this latest migration
    const hasLatestMigration = currentBranchMigrations.some(
      (migration) => migration.version === latestMainMigration.version
    )

    return !hasLatestMigration
  }, [currentBranchMigrations, mainBranchMigrations])

  // Check if main branch has functions that are newer than the current branch versions
  const outOfDateFunctionsCount = useMemo(() => {
    if (!edgeFunctionsDiff.modifiedSlugs.length) return 0

    return edgeFunctionsDiff.modifiedSlugs.filter((slug) => {
      // Get both main and current branch function data
      const mainFunction = edgeFunctionsDiff.mainBranchFunctions?.find((func) => func.slug === slug)
      const currentFunction = edgeFunctionsDiff.currentBranchFunctions?.find(
        (func) => func.slug === slug
      )

      if (!mainFunction || !currentFunction) return false

      // Compare updated_at timestamps - if main branch function is newer, it was modified after current branch
      const mainUpdatedAt = mainFunction.updated_at * 1000 // Convert to milliseconds
      const currentUpdatedAt = currentFunction.updated_at * 1000 // Convert to milliseconds

      return mainUpdatedAt > currentUpdatedAt
    }).length
  }, [
    edgeFunctionsDiff.modifiedSlugs,
    edgeFunctionsDiff.mainBranchFunctions,
    edgeFunctionsDiff.currentBranchFunctions,
  ])

  // Check if main branch has functions that are newer than the current branch versions
  const hasOutOfDateFunctions = outOfDateFunctionsCount > 0

  // Check if current branch has any edge function modifications (not additions/removals)
  // This only includes functions where the current branch version is newer than the main branch version
  const hasEdgeFunctionModifications =
    edgeFunctionsDiff.modifiedSlugs.length > outOfDateFunctionsCount

  // Count of removed functions that were updated on main branch after this branch was created
  // Note: For removed functions, we use branch creation date since there's no current branch version to compare to
  const missingFunctionsCount = useMemo(() => {
    if (!currentBranchCreatedAt || !edgeFunctionsDiff.removedSlugs.length) return 0

    const branchCreatedAt = new Date(currentBranchCreatedAt).getTime()

    return edgeFunctionsDiff.removedSlugs.filter((slug) => {
      // Access main branch function data from the original functions list
      const mainFunction = edgeFunctionsDiff.mainBranchFunctions?.find((func) => func.slug === slug)
      if (!mainFunction) return false

      // Check if function was updated after branch creation
      const functionUpdatedAt = mainFunction.updated_at * 1000 // Convert to milliseconds
      return functionUpdatedAt > branchCreatedAt
    }).length
  }, [
    currentBranchCreatedAt,
    edgeFunctionsDiff.removedSlugs,
    edgeFunctionsDiff.mainBranchFunctions,
  ])

  // Check if main branch has functions removed from current branch that were updated after branch creation
  const hasMissingFunctions = missingFunctionsCount > 0

  // Update overall out-of-date check to include newer removed functions and newer modified functions
  const isBranchOutOfDateOverall =
    isBranchOutOfDateMigrations || hasMissingFunctions || hasOutOfDateFunctions

  // Get the count of migrations that the branch is missing
  const missingMigrationsCount = useMemo(() => {
    if (!currentBranchMigrations || !mainBranchMigrations || !isBranchOutOfDateMigrations) return 0

    const currentVersions = new Set(currentBranchMigrations.map((m) => m.version))
    return mainBranchMigrations.filter((m) => !currentVersions.has(m.version)).length
  }, [currentBranchMigrations, mainBranchMigrations, isBranchOutOfDateMigrations])

  // Get count of modified functions
  const modifiedFunctionsCount = edgeFunctionsDiff.modifiedSlugs.length

  // Check if there are any changes (database or edge functions)
  const hasChanges = useMemo(() => {
    // Check database changes
    const hasDatabaseChanges = diffContent && diffContent.trim() !== ''

    // Check edge function changes
    const hasEdgeFunctionChanges = edgeFunctionsDiff.hasChanges

    return hasDatabaseChanges || hasEdgeFunctionChanges
  }, [diffContent, edgeFunctionsDiff.hasChanges])

  const isLoading = isDatabaseDiffLoading || edgeFunctionsDiff.isLoading

  return {
    // Database diff
    diffContent,
    isDatabaseDiffLoading,
    isDatabaseDiffRefetching,
    databaseDiffError,
    refetchDatabaseDiff,

    // Edge functions diff
    edgeFunctionsDiff,

    // Migrations
    currentBranchMigrations,
    mainBranchMigrations,
    refetchCurrentBranchMigrations,
    refetchMainBranchMigrations,

    // Branch state
    isBranchOutOfDateMigrations,
    hasEdgeFunctionModifications,
    missingFunctionsCount,
    hasMissingFunctions,
    outOfDateFunctionsCount,
    hasOutOfDateFunctions,
    isBranchOutOfDateOverall,
    missingMigrationsCount,
    modifiedFunctionsCount,

    // Combined states
    isLoading,
    hasChanges,
  }
}
