<template>
  <div class="flex flex-col h-full">
    <!-- Submissions History (Top) -->
    <div class="flex-1 overflow-y-auto p-2">
      <div v-if="submissions.length === 0" class="flex flex-col items-center justify-center h-full text-muted-foreground">
        <Upload class="h-10 w-10 mb-2 opacity-50" />
        <p class="text-sm">No submissions yet. Submit your work below.</p>
      </div>

      <div v-else class="space-y-2">
        <SubmissionCard
          v-for="submission in submissions"
          :key="submission.id"
          :task-id="taskId"
          :submission="submission"
          @view="handleView"
          @submission-updated="emit('submissionsUpdated')"
        />
      </div>
    </div>

    <!-- Submit Work Form (Bottom) -->
    <div class="flex-shrink-0 border-t bg-background p-2">
      <div class="space-y-2">
        <div v-if="submissionConfig" class="text-xs text-muted-foreground space-y-0.5 px-1">
          <div class="flex items-center gap-1.5">
            <span v-if="submissionConfig.allowed_extensions.length > 0">
              Allowed: {{ submissionConfig.allowed_extensions.join(', ') }}
            </span>
            <Badge v-if="submissionConfig.required" variant="outline" class="text-[10px] px-1 py-0">Required</Badge>
          </div>
          <div v-if="submissionConfig.naming_pattern && submissionConfig.check_naming">
            {{ submissionConfig.naming_pattern_is_regex ? 'Naming (regex):' : 'Naming:' }}
            <code>{{ submissionConfig.naming_pattern }}</code>
          </div>
          <div v-if="submissionConfig.check_movie_spec && (submissionConfig.movie_resolution || submissionConfig.movie_format || submissionConfig.movie_codec || submissionConfig.movie_frame_rate)">
            Movie spec:
            <span v-if="submissionConfig.movie_resolution">{{ submissionConfig.movie_resolution }}</span>
            <span v-if="submissionConfig.movie_format">.{{ submissionConfig.movie_format }}</span>
            <span v-if="submissionConfig.movie_codec">({{ submissionConfig.movie_codec }})</span>
            <span v-if="submissionConfig.movie_frame_rate">{{ submissionConfig.movie_frame_rate }}fps</span>
          </div>
        </div>

        <div class="border-2 border-dashed rounded-lg p-3 text-center hover:border-primary/50 transition-colors">
          <input
            ref="fileInput"
            type="file"
            class="hidden"
            @change="handleFileSelect"
          />
          <Upload class="h-6 w-6 mx-auto mb-1 text-muted-foreground" />
          <p class="text-xs font-medium mb-1">Submit Work</p>
          <Button
            variant="outline"
            size="sm"
            @click="fileInput?.click()"
            :disabled="uploading"
            class="h-7 text-xs"
          >
            <Upload class="h-3 w-3 mr-1" />
            {{ uploading ? 'Uploading...' : 'Choose File' }}
          </Button>
        </div>

        <Textarea
          v-model="submissionNotes"
          placeholder="Add notes about this submission..."
          rows="2"
          class="resize-none text-sm"
        />
      </div>
    </div>

    <!-- Media Viewer Dialog -->
    <Dialog v-model:open="viewerOpen">
      <DialogContent class="max-w-6xl max-h-[90vh]">
        <DialogHeader>
          <DialogTitle>
            {{ selectedSubmission?.file_name }} (v{{ selectedSubmission?.version_number }})
          </DialogTitle>
          <DialogDescription>
            Submission preview and details
          </DialogDescription>
        </DialogHeader>
        <div class="flex items-center justify-center overflow-auto">
          <!-- Video Player -->
          <video
            v-if="selectedSubmission?.stream_url && mediaBlobUrl"
            :src="mediaBlobUrl"
            controls
            class="max-w-full max-h-[70vh]"
          />
          <!-- Image Viewer -->
          <img
            v-else-if="selectedSubmission?.thumbnail_url && mediaBlobUrl"
            :src="mediaBlobUrl"
            :alt="selectedSubmission.file_name"
            class="max-w-full max-h-[70vh] object-contain"
          />
          <!-- Fallback for other file types -->
          <div v-else class="text-center p-8">
            <p class="text-muted-foreground mb-4">Preview not available for this file type</p>
            <Button @click="handleDownload(selectedSubmission)" variant="outline">
              Download File
            </Button>
          </div>
        </div>
        <div v-if="selectedSubmission?.notes" class="mt-4 p-4 bg-muted rounded-lg">
          <p class="text-sm font-semibold mb-1">Submission Notes:</p>
          <p class="text-sm whitespace-pre-wrap">{{ selectedSubmission.notes }}</p>
        </div>
        <div v-if="selectedSubmission?.latest_review" class="mt-4 p-4 border rounded-lg">
          <div class="flex items-center justify-between mb-2">
            <p class="text-sm font-semibold">Review</p>
            <Badge :variant="selectedSubmission.latest_review.decision === 'approved' ? 'default' : 'destructive'">
              {{ selectedSubmission.latest_review.decision }}
            </Badge>
          </div>
          <p class="text-sm text-muted-foreground mb-1">
            By {{ selectedSubmission.latest_review.reviewer_first_name }} {{ selectedSubmission.latest_review.reviewer_last_name }}
          </p>
          <p v-if="selectedSubmission.latest_review.feedback" class="text-sm whitespace-pre-wrap">
            {{ selectedSubmission.latest_review.feedback }}
          </p>
        </div>
      </DialogContent>
    </Dialog>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, watch } from 'vue'
import { Upload } from 'lucide-vue-next'
import { Button } from '@/components/ui/button'
import { Textarea } from '@/components/ui/textarea'
import { Badge } from '@/components/ui/badge'
import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogDescription,
} from '@/components/ui/dialog'
import SubmissionCard from './SubmissionCard.vue'
import { taskService, type Submission } from '@/services/task'
import { projectService, type SubmissionTypeConfig } from '@/services/project'
import { useToast } from '@/components/ui/toast/use-toast'
import { apiClient } from '@/services/api'
import mediaInfoFactory, { isTrackType } from 'mediainfo.js'
import mediaInfoWasmUrl from 'mediainfo.js/MediaInfoModule.wasm?url'

const props = defineProps<{
  taskId: number
  submissions: Submission[]
  taskType?: string
  projectId?: number
  name?: string
  taskName?: string
  projectName?: string
}>()

const emit = defineEmits<{
  submissionsUpdated: []
}>()

const { toast } = useToast()

const fileInput = ref<HTMLInputElement>()
const uploading = ref(false)
const submissionNotes = ref('')
const viewerOpen = ref(false)
const selectedSubmission = ref<Submission | null>(null)
const mediaBlobUrl = ref<string | null>(null)
const submissionConfig = ref<SubmissionTypeConfig | null>(null)
const projectCode = ref<string | null>(null)

async function loadSubmissionConfig() {
  submissionConfig.value = null
  projectCode.value = null
  if (!props.projectId || !props.taskType) return
  try {
    const config = await projectService.getProjectSubmissionConfig(props.projectId)
    submissionConfig.value = config.config_by_task_type[props.taskType] || null
    if (submissionConfig.value?.naming_pattern?.includes('{project_code}')) {
      const project = await projectService.getProject(props.projectId)
      projectCode.value = project.code_name
    }
  } catch (error) {
    console.error('Failed to load submission configuration:', error)
  }
}

onMounted(loadSubmissionConfig)
watch(() => [props.projectId, props.taskType], loadSubmissionConfig)

const MOVIE_EXTENSIONS = ['.mov', '.mp4', '.avi', '.mkv', '.webm', '.mxf']

let mediaInfoPromise: ReturnType<typeof mediaInfoFactory> | null = null
function getMediaInfo() {
  if (!mediaInfoPromise) {
    mediaInfoPromise = mediaInfoFactory({ locateFile: () => mediaInfoWasmUrl })
  }
  return mediaInfoPromise
}

interface VideoTrackInfo {
  width: number | null
  height: number | null
  format: string | null
  codecId: string | null
  formatCommercial: string | null
  frameRate: number | null
}

// Parses container/codec metadata directly from the file's bytes (no playback) - works for
// containers browsers can't play natively, e.g. MXF, or codecs like ProRes/DNxHD
async function analyzeVideoFile(file: File): Promise<VideoTrackInfo | null> {
  try {
    const mediainfo = await getMediaInfo()
    const getSize = () => file.size
    const readChunk = (chunkSize: number, offset: number) =>
      new Promise<Uint8Array>((resolve, reject) => {
        const reader = new FileReader()
        reader.onload = () => resolve(new Uint8Array(reader.result as ArrayBuffer))
        reader.onerror = () => reject(reader.error)
        reader.readAsArrayBuffer(file.slice(offset, offset + chunkSize))
      })
    const result = await mediainfo.analyzeData(getSize, readChunk)
    const videoTrack = result.media?.track.find(t => isTrackType(t, 'Video'))
    if (!videoTrack) return null
    const track = videoTrack as unknown as Record<string, unknown>
    const commercial = [track.Format_Commercial, track.Format_Commercial_IfAny]
      .filter((v): v is string => typeof v === 'string')
      .join(' ')
    return {
      width: videoTrack.Width ?? null,
      height: videoTrack.Height ?? null,
      format: videoTrack.Format ?? null,
      codecId: videoTrack.CodecID ?? null,
      formatCommercial: commercial || null,
      frameRate: videoTrack.FrameRate ?? null
    }
  } catch (error) {
    console.error('Failed to analyze video file:', error)
    return null
  }
}

const CODEC_PATTERNS: Record<string, RegExp> = {
  h264: /\b(avc|h\.?264)\b/i,
  h265: /\b(hevc|h\.?265)\b/i,
  mjpeg: /\bm?jpeg\b/i,
  dnxhd: /\bdnxhd\b/i,
  dnxhr: /\bdnxhr\b/i,
  prores: /\bprores\b/i,
  uncompressed: /\b(uncompressed|raw)\b/i,
  avid: /\bavid\b/i,
  cineform: /\bcineform\b/i
}

function normalizeCodec(info: VideoTrackInfo): string | null {
  const haystack = [info.format, info.formatCommercial, info.codecId].filter(Boolean).join(' ')
  for (const [key, pattern] of Object.entries(CODEC_PATTERNS)) {
    if (pattern.test(haystack)) return key
  }
  return null
}

// All checking happens client-side, entirely in the browser, before the file is ever uploaded
async function findSubmissionViolation(file: File): Promise<string | null> {
  const config = submissionConfig.value
  if (!config) return null

  const filename = file.name
  const dotIndex = filename.lastIndexOf('.')
  const stem = dotIndex > 0 ? filename.slice(0, dotIndex) : filename
  const extension = dotIndex > 0 ? filename.slice(dotIndex).toLowerCase() : ''

  if (config.allowed_extensions.length > 0 && !config.allowed_extensions.includes(extension)) {
    return `File type '${extension}' is not accepted for ${props.taskType} submissions. Allowed types: ${config.allowed_extensions.join(', ')}`
  }

  if (config.check_naming && config.naming_pattern) {
    if (config.naming_pattern_is_regex) {
      try {
        const regex = new RegExp(config.naming_pattern)
        if (!regex.test(stem)) {
          return `Filename does not match the required pattern '${config.naming_pattern}'`
        }
      } catch (error) {
        console.error('Invalid naming pattern regex:', error)
      }
    } else {
      const tokenValues: Record<string, string | undefined> = {
        name: props.name,
        task_type: props.taskType,
        task_name: props.taskName,
        project_name: props.projectName,
        project_code: projectCode.value || undefined
      }
      const usedTokens = config.naming_pattern.match(/\{(name|task_name|project_name|project_code)\}/g) || []
      const unresolvable = usedTokens.some(token => !tokenValues[token.slice(1, -1)])
      if (!unresolvable) {
        const escapeRegex = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
        const pattern = config.naming_pattern
          .split(/(\{name\}|\{task_type\}|\{task_name\}|\{project_name\}|\{project_code\}|\{version\})/)
          .map(part => {
            if (part === '{version}') return '\\d{3}'
            const key = part.startsWith('{') && part.endsWith('}') ? part.slice(1, -1) : null
            if (key && key in tokenValues) return escapeRegex(tokenValues[key] || '')
            return escapeRegex(part)
          })
          .join('')
        const regex = new RegExp(`^${pattern}$`)
        if (!regex.test(stem)) {
          const example = config.naming_pattern
            .replace('{name}', props.name || 'name')
            .replace('{task_name}', props.taskName || 'taskname')
            .replace('{project_name}', props.projectName || 'project')
            .replace('{project_code}', projectCode.value || 'CODE')
            .replace('{task_type}', props.taskType || '')
            .replace('{version}', '001')
          return `Filename does not match the required naming convention '${config.naming_pattern}' (e.g. '${example}${extension}')`
        }
      }
      // If a token couldn't be resolved client-side, don't false-block
    }
  }

  if (config.check_movie_spec && MOVIE_EXTENSIONS.includes(extension)) {
    if (config.movie_format && extension.slice(1) !== config.movie_format.toLowerCase()) {
      return `Movie format '${extension}' does not match the required format '.${config.movie_format}'`
    }
    if (config.movie_resolution || config.movie_codec || config.movie_frame_rate) {
      const info = await analyzeVideoFile(file)
      if (info) {
        if (config.movie_resolution && info.width && info.height) {
          const actual = `${info.width}x${info.height}`
          if (actual !== config.movie_resolution) {
            return `Video resolution ${actual} does not match the required resolution ${config.movie_resolution}`
          }
        }
        if (config.movie_codec) {
          const actualCodec = normalizeCodec(info)
          if (actualCodec && actualCodec !== config.movie_codec) {
            return `Video codec '${actualCodec}' does not match the required codec '${config.movie_codec}'`
          }
          // If the codec couldn't be identified, don't false-block
        }
        if (config.movie_frame_rate && info.frameRate) {
          if (Math.abs(info.frameRate - config.movie_frame_rate) > 0.05) {
            return `Video frame rate ${info.frameRate}fps does not match the required frame rate ${config.movie_frame_rate}fps`
          }
        }
      }
      // If the file couldn't be analyzed at all, don't false-block
    }
  }

  return null
}

async function handleFileSelect(event: Event) {
  const target = event.target as HTMLInputElement
  const file = target.files?.[0]
  if (!file) return

  const violation = await findSubmissionViolation(file)
  if (violation) {
    toast({
      title: 'Invalid submission',
      description: violation,
      variant: 'destructive'
    })
    if (fileInput.value) {
      fileInput.value.value = ''
    }
    return
  }

  uploading.value = true
  try {
    await taskService.submitWork(
      props.taskId,
      file,
      submissionNotes.value || undefined
    )
    submissionNotes.value = ''
    emit('submissionsUpdated')
    toast({
      title: 'Success',
      description: 'Work submitted successfully'
    })
    // Reset input
    if (fileInput.value) {
      fileInput.value.value = ''
    }
  } catch (error: any) {
    console.error('Error submitting work:', error)
    toast({
      title: 'Error',
      description: error.response?.data?.detail || 'Failed to submit work',
      variant: 'destructive'
    })
  } finally {
    uploading.value = false
  }
}

async function handleView(submission: Submission) {
  selectedSubmission.value = submission
  viewerOpen.value = true
  
  // Load media for viewer
  if (submission.thumbnail_url || submission.stream_url) {
    await loadMediaForViewer(submission)
  }
}

async function loadMediaForViewer(submission: Submission) {
  try {
    if (submission.stream_url) {
      // Load video
      const response = await apiClient.get(submission.stream_url, {
        responseType: 'blob'
      })
      if (mediaBlobUrl.value) {
        URL.revokeObjectURL(mediaBlobUrl.value)
      }
      mediaBlobUrl.value = URL.createObjectURL(response.data)
    } else if (submission.download_url) {
      // Load image
      const response = await apiClient.get(submission.download_url, {
        responseType: 'blob'
      })
      if (mediaBlobUrl.value) {
        URL.revokeObjectURL(mediaBlobUrl.value)
      }
      mediaBlobUrl.value = URL.createObjectURL(response.data)
    }
  } catch (error) {
    console.error('Failed to load media:', error)
    mediaBlobUrl.value = null
  }
}

function getFileUrl(url: string | null | undefined) {
  if (!url) return ''
  if (url.startsWith('http')) return url
  // Remove any leading slashes and backend prefix
  const cleanUrl = url.replace(/^backend[\/\\]/, '').replace(/\\/g, '/').replace(/^\/+/, '')
  return `http://localhost:8000/${cleanUrl}`
}

async function handleDownload(submission: Submission | null) {
  if (!submission?.download_url) return
  
  try {
    const response = await apiClient.get(submission.download_url, {
      responseType: 'blob'
    })
    
    // Create download link
    const url = URL.createObjectURL(response.data)
    const link = document.createElement('a')
    link.href = url
    link.download = submission.file_name
    document.body.appendChild(link)
    link.click()
    document.body.removeChild(link)
    URL.revokeObjectURL(url)
  } catch (error) {
    console.error('Failed to download file:', error)
  }
}
</script>
