import React, { useState, useEffect, useCallback } from 'react'
import { useWebSocket } from './hooks/useWebSocket'
import { visionApi, type SystemStatus, type CameraStatus, type RecordingInfo } from './services/api'
import { SystemHealthWidget } from './widgets/SystemHealthWidget'
import { MqttStatusWidget } from './widgets/MqttStatusWidget'
import { RecordingsCountWidget } from './widgets/RecordingsCountWidget'
import { CameraCountWidget } from './widgets/CameraCountWidget'
import { CameraCard } from './components/CameraCard'
import { CameraPreviewModal } from './components/CameraPreviewModal'
import { CameraConfigModal } from './components/CameraConfigModal'
import { MqttDebugPanel } from './components/MqttDebugPanel'

// Get WebSocket URL from environment or construct it
const getWebSocketUrl = () => {
  const apiUrl = import.meta.env.VITE_VISION_API_URL || '/api'

  // If it's a relative path, use relative WebSocket URL
  if (apiUrl.startsWith('/')) {
    const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
    return `${protocol}//${window.location.host}${apiUrl.replace(/\/api$/, '')}/ws`
  }

  // Convert http(s):// to ws(s)://
  const wsUrl = apiUrl.replace(/^http/, 'ws')
  return `${wsUrl.replace(/\/api$/, '')}/ws`
}

export default function App() {
  const [systemStatus, setSystemStatus] = useState<SystemStatus | null>(null)
  const [cameras, setCameras] = useState<Record<string, CameraStatus>>({})
  const [recordings, setRecordings] = useState<Record<string, RecordingInfo>>({})
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState<string | null>(null)
  const [lastUpdate, setLastUpdate] = useState<Date | null>(null)
  const [notification, setNotification] = useState<{ type: 'success' | 'error', message: string } | null>(null)

  // Modal states
  const [previewModalOpen, setPreviewModalOpen] = useState(false)
  const [previewCamera, setPreviewCamera] = useState<string | null>(null)
  const [configModalOpen, setConfigModalOpen] = useState(false)
  const [selectedCamera, setSelectedCamera] = useState<string | null>(null)
  const [debugPanelOpen, setDebugPanelOpen] = useState(false)

  // WebSocket connection
  const { isConnected, subscribe } = useWebSocket(getWebSocketUrl())

  // Fetch initial data
  const fetchInitialData = useCallback(async () => {
    try {
      setError(null)
      const [status, camerasData, recordingsData] = await Promise.all([
        visionApi.getSystemStatus(),
        visionApi.getCameras(),
        visionApi.getRecordings(),
      ])

      setSystemStatus(status)
      setCameras(camerasData)
      setRecordings(recordingsData)
      setLastUpdate(new Date())
    } catch (err) {
      // Don't set error state - let widgets show API is unavailable
      // Keep existing state so UI can still render
      console.error('Failed to fetch initial data:', err)
      setLastUpdate(new Date()) // Update timestamp even on error to show we tried
    } finally {
      setLoading(false)
    }
  }, [])

  // Set up WebSocket subscriptions for real-time updates
  useEffect(() => {
    const unsubscribeFunctions: Array<() => void> = []

    // Subscribe to camera status changes
    unsubscribeFunctions.push(
      subscribe('camera_status_changed', (event) => {
        const { camera_name, status, is_recording } = event.data
        setCameras((prev) => ({
          ...prev,
          [camera_name]: {
            ...prev[camera_name],
            status,
            is_recording,
            last_checked: new Date().toISOString(),
          },
        }))
        setLastUpdate(new Date())
      })
    )

    // Subscribe to recording started events
    unsubscribeFunctions.push(
      subscribe('recording_started', (event) => {
        const { camera_name } = event.data
        setCameras((prev) => ({
          ...prev,
          [camera_name]: {
            ...prev[camera_name],
            is_recording: true,
          },
        }))

        // Refresh recordings to get accurate count
        visionApi.getRecordings().then(setRecordings).catch(console.error)

        // Refresh system status to update counts
        visionApi.getSystemStatus().then(setSystemStatus).catch(console.error)

        setLastUpdate(new Date())
      })
    )

    // Subscribe to recording stopped events
    unsubscribeFunctions.push(
      subscribe('recording_stopped', (event) => {
        const { camera_name } = event.data
        setCameras((prev) => ({
          ...prev,
          [camera_name]: {
            ...prev[camera_name],
            is_recording: false,
          },
        }))

        // Refresh recordings and system status
        Promise.all([
          visionApi.getRecordings(),
          visionApi.getSystemStatus(),
        ]).then(([recordingsData, statusData]) => {
          setRecordings(recordingsData)
          setSystemStatus(statusData)
        }).catch(console.error)

        setLastUpdate(new Date())
      })
    )

    // Subscribe to system status changes
    unsubscribeFunctions.push(
      subscribe('system_status_changed', () => {
        visionApi.getSystemStatus().then(setSystemStatus).catch(console.error)
        setLastUpdate(new Date())
      })
    )

    // Subscribe to MQTT status changes
    unsubscribeFunctions.push(
      subscribe('mqtt_status_changed', () => {
        visionApi.getSystemStatus().then(setSystemStatus).catch(console.error)
        setLastUpdate(new Date())
      })
    )

    return () => {
      unsubscribeFunctions.forEach((unsub) => unsub())
    }
  }, [subscribe])

  // Fetch initial data on mount
  useEffect(() => {
    fetchInitialData()
  }, [fetchInitialData])

  // Camera action handlers
  const handleStartRecording = useCallback(async (cameraName: string) => {
    try {
      const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
      const filename = `manual_${cameraName}_${timestamp}.mp4`
      const result = await visionApi.startRecording(cameraName, filename)

      if (result.success) {
        setNotification({ type: 'success', message: `Recording started: ${result.filename}` })

        // Immediately update state optimistically (UI updates instantly)
        setCameras((prev) => ({
          ...prev,
          [cameraName]: {
            ...prev[cameraName],
            is_recording: true,
            current_recording_file: result.filename,
          },
        }))

        // Refresh camera status from API as backup (in case WebSocket is delayed)
        setTimeout(() => {
          visionApi.getCameras().then(setCameras).catch(console.error)
        }, 500)
      } else {
        setNotification({ type: 'error', message: `Failed: ${result.message}` })
      }
    } catch (err) {
      setNotification({ type: 'error', message: err instanceof Error ? err.message : 'Unknown error' })
    }
  }, [])

  const handleStopRecording = useCallback(async (cameraName: string) => {
    try {
      const result = await visionApi.stopRecording(cameraName)
      if (result.success) {
        setNotification({ type: 'success', message: 'Recording stopped' })

        // Immediately update state optimistically (UI updates instantly)
        setCameras((prev) => ({
          ...prev,
          [cameraName]: {
            ...prev[cameraName],
            is_recording: false,
            current_recording_file: null,
          },
        }))

        // Refresh camera status from API as backup (in case WebSocket is delayed)
        setTimeout(() => {
          visionApi.getCameras().then(setCameras).catch(console.error)
        }, 500)
      } else {
        setNotification({ type: 'error', message: `Failed: ${result.message}` })
      }
    } catch (err) {
      setNotification({ type: 'error', message: err instanceof Error ? err.message : 'Unknown error' })
    }
  }, [])

  const handlePreviewModal = useCallback((cameraName: string) => {
    setPreviewCamera(cameraName)
    setPreviewModalOpen(true)
    // The modal will start streaming and notify us via onStreamStarted callback
  }, [])

  const handlePreviewNewWindow = useCallback(async (cameraName: string) => {
    try {
      // Start streaming before opening new window
      const result = await visionApi.startStream(cameraName)
      if (result.success) {
        // Immediately update camera status to show "Stop Streaming" button
        setCameras((prev) => ({
          ...prev,
          [cameraName]: {
            ...prev[cameraName],
            status: 'streaming',
          },
        }))

        // Open camera stream in new window/tab
        const streamUrl = visionApi.getStreamUrl(cameraName)
        window.open(streamUrl, '_blank')
      } else {
        setNotification({ type: 'error', message: `Failed to start stream: ${result.message}` })
      }
    } catch (err) {
      setNotification({ type: 'error', message: err instanceof Error ? err.message : 'Failed to start stream' })
    }
  }, [])

  const handleConfigure = useCallback((cameraName: string) => {
    setSelectedCamera(cameraName)
    setConfigModalOpen(true)
  }, [])

  const handleRestart = useCallback(async (cameraName: string) => {
    try {
      setNotification({ type: 'success', message: `Restarting camera ${cameraName}...` })
      const result = await visionApi.reinitializeCamera(cameraName)

      if (result.success) {
        setNotification({ type: 'success', message: `Camera ${cameraName} restarted successfully` })
        // Refresh camera status
        setTimeout(() => {
          visionApi.getCameras().then(setCameras).catch(console.error)
          visionApi.getSystemStatus().then(setSystemStatus).catch(console.error)
        }, 2000) // Wait 2 seconds for camera to reinitialize
      } else {
        setNotification({ type: 'error', message: `Failed: ${result.message}` })
      }
    } catch (err) {
      setNotification({ type: 'error', message: err instanceof Error ? err.message : 'Unknown error' })
    }
  }, [])

  const handleStopStreaming = useCallback(async (cameraName: string) => {
    try {
      const result = await visionApi.stopStream(cameraName)
      if (result.success) {
        setNotification({ type: 'success', message: 'Streaming stopped' })

        // Immediately update camera status (UI updates instantly)
        setCameras((prev) => ({
          ...prev,
          [cameraName]: {
            ...prev[cameraName],
            status: 'available',
          },
        }))

        // Refresh camera status from API as backup
        setTimeout(() => {
          visionApi.getCameras().then(setCameras).catch(console.error)
        }, 500)
      } else {
        setNotification({ type: 'error', message: `Failed: ${result.message}` })
      }
    } catch (err) {
      setNotification({ type: 'error', message: err instanceof Error ? err.message : 'Unknown error' })
    }
  }, [])

  // Auto-hide notifications
  useEffect(() => {
    if (notification) {
      const timer = setTimeout(() => setNotification(null), 5000)
      return () => clearTimeout(timer)
    }
  }, [notification])

  if (loading) {
    return (
      <div className="p-6">
        <div className="flex items-center justify-center h-64">
          <div className="text-center">
            <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-indigo-600 mx-auto" />
            <p className="mt-4 text-gray-600">Loading vision system...</p>
          </div>
        </div>
      </div>
    )
  }

  if (error) {
    return (
      <div className="p-6">
        <div className="bg-red-50 border border-red-200 rounded-md p-4">
          <div className="flex">
            <div className="flex-shrink-0">
              <svg className="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
                <path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
              </svg>
            </div>
            <div className="ml-3">
              <h3 className="text-sm font-medium text-red-800">Error loading vision system</h3>
              <div className="mt-2 text-sm text-red-700">
                <p>{error}</p>
              </div>
              <div className="mt-4">
                <button
                  onClick={fetchInitialData}
                  className="bg-red-100 px-3 py-2 rounded-md text-sm font-medium text-red-800 hover:bg-red-200"
                >
                  Retry
                </button>
              </div>
            </div>
          </div>
        </div>
      </div>
    )
  }

  const cameraCount = Object.keys(cameras).length
  const machineCount = systemStatus ? Object.keys(systemStatus.machines).length : 0
  const activeRecordings = systemStatus?.active_recordings ?? 0
  // Fix: Use recordings object length instead of total_recordings (which may be incorrect)
  const totalRecordings = Object.keys(recordings).length

  return (
    <div className="p-6 space-y-6">
      {/* Header */}
      <div className="flex items-center justify-between">
        <div>
          <h1 className="text-3xl font-bold text-gray-900">Vision System</h1>
          <p className="mt-2 text-gray-600">Monitor cameras, machines, and recording status</p>
          {lastUpdate && (
            <p className="mt-1 text-sm text-gray-500 flex items-center space-x-2">
              <span>Last updated: {lastUpdate.toLocaleTimeString()}</span>
              {isConnected ? (
                <span className="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
                  <span className="w-2 h-2 bg-green-500 rounded-full mr-1 animate-pulse" />
                  Live Updates
                </span>
              ) : (
                <span className="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800">
                  Polling Mode
                </span>
              )}
            </p>
          )}
        </div>
      </div>

      {/* Status Widgets */}
      <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
        <SystemHealthWidget systemStatus={systemStatus} />
        <MqttStatusWidget
          systemStatus={systemStatus}
          onDebugClick={() => setDebugPanelOpen(true)}
        />
        <RecordingsCountWidget active={activeRecordings} total={totalRecordings} />
        <CameraCountWidget cameraCount={cameraCount} machineCount={machineCount} />
      </div>

      {/* Cameras Grid */}
      <div className="bg-white shadow rounded-lg">
        <div className="px-4 py-5 sm:px-6 border-b border-gray-200">
          <h3 className="text-lg leading-6 font-medium text-gray-900">Cameras</h3>
          <p className="mt-1 max-w-2xl text-sm text-gray-500">
            Current status of all cameras in the system
          </p>
        </div>
        <div className="p-6">
          <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
            {Object.entries(cameras).map(([cameraName, camera]) => (
              <CameraCard
                key={cameraName}
                cameraName={cameraName}
                camera={camera}
                onStartRecording={handleStartRecording}
                onStopRecording={handleStopRecording}
                onPreviewModal={handlePreviewModal}
                onPreviewNewWindow={handlePreviewNewWindow}
                onStopStreaming={handleStopStreaming}
                onConfigure={handleConfigure}
                onRestart={handleRestart}
              />
            ))}
          </div>
        </div>
      </div>

      {/* Notification */}
      {notification && (
        <div
          className={`fixed top-4 right-4 z-[999999] p-4 rounded-md shadow-lg ${notification.type === 'success'
              ? 'bg-green-50 border border-green-200 text-green-800'
              : 'bg-red-50 border border-red-200 text-red-800'
            }`}
        >
          <div className="flex items-center">
            <div className="flex-shrink-0">
              {notification.type === 'success' ? (
                <svg className="h-5 w-5 text-green-400" viewBox="0 0 20 20" fill="currentColor">
                  <path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
                </svg>
              ) : (
                <svg className="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor">
                  <path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
                </svg>
              )}
            </div>
            <div className="ml-3">
              <p className="text-sm font-medium">{notification.message}</p>
            </div>
            <div className="ml-auto pl-3">
              <button
                onClick={() => setNotification(null)}
                className={`inline-flex rounded-md p-1.5 focus:outline-none focus:ring-2 focus:ring-offset-2 ${notification.type === 'success'
                    ? 'text-green-500 hover:bg-green-100 focus:ring-green-600'
                    : 'text-red-500 hover:bg-red-100 focus:ring-red-600'
                  }`}
              >
                <svg className="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
                  <path fillRule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clipRule="evenodd" />
                </svg>
              </button>
            </div>
          </div>
        </div>
      )}

      {/* Camera Preview Modal */}
      {previewCamera && (
        <CameraPreviewModal
          cameraName={previewCamera}
          isOpen={previewModalOpen}
          onClose={() => {
            setPreviewModalOpen(false)
            setPreviewCamera(null)
          }}
          onStreamStarted={() => {
            // Update camera status when streaming starts
            setCameras((prev) => ({
              ...prev,
              [previewCamera]: {
                ...prev[previewCamera],
                status: 'streaming',
              },
            }))
          }}
          onStreamStopped={() => {
            // Update camera status when streaming stops
            setCameras((prev) => ({
              ...prev,
              [previewCamera]: {
                ...prev[previewCamera],
                status: 'available',
              },
            }))
          }}
        />
      )}

      {/* Camera Configuration Modal */}
      {selectedCamera && (
        <CameraConfigModal
          cameraName={selectedCamera}
          isOpen={configModalOpen}
          onClose={() => {
            setConfigModalOpen(false)
            setSelectedCamera(null)
          }}
          onSuccess={(message) => {
            setNotification({ type: 'success', message })
            // Refresh camera status
            visionApi.getCameras().then(setCameras).catch(console.error)
          }}
          onError={(error) => {
            setNotification({ type: 'error', message: error })
          }}
        />
      )}

      {/* MQTT Debug Panel */}
      <MqttDebugPanel
        isOpen={debugPanelOpen}
        onClose={() => setDebugPanelOpen(false)}
      />
    </div>
  )
}

