<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import { api } from '@/api/client'
import { useDownloadsStore } from '@/stores/downloads'
import FormatPicker from './FormatPicker.vue'
import type { FormatInfo, UrlInfo } from '@/api/types'

const store = useDownloadsStore()

const url = ref('')
const formats = ref<FormatInfo[]>([])
const selectedFormatId = ref<string | null>(null)
const isExtracting = ref(false)
const extractError = ref<string | null>(null)
const showOptions = ref(false)

// URL preview state
const urlInfo = ref<UrlInfo | null>(null)
const isLoadingInfo = ref(false)
const audioLocked = ref(false)  // true when source is audio-only

type MediaType = 'video' | 'audio'
const mediaType = ref<MediaType>('video')
const outputFormat = ref<string>('auto')

const audioFormats = ['auto', 'mp3', 'wav', 'm4a', 'flac', 'opus'] as const
const videoFormats = ['auto', 'mp4', 'webm'] as const

const availableFormats = computed(() =>
  mediaType.value === 'audio' ? audioFormats : videoFormats
)

// Reset output format when switching media type
watch(mediaType, () => {
  outputFormat.value = 'auto'
})

/** Whether formats have been fetched for the current URL. */
const formatsReady = computed(() => formats.value.length > 0)

async function extractFormats(): Promise<void> {
  const trimmed = url.value.trim()
  if (!trimmed) return

  isExtracting.value = true
  extractError.value = null
  formats.value = []
  selectedFormatId.value = null

  try {
    formats.value = await api.getFormats(trimmed)
  } catch (err: any) {
    extractError.value = err.message || 'Failed to extract formats'
  } finally {
    isExtracting.value = false
  }
}

async function submitDownload(): Promise<void> {
  const trimmed = url.value.trim()
  if (!trimmed) return

  try {
    await store.submitDownload({
      url: trimmed,
      format_id: selectedFormatId.value,
      quality: mediaType.value === 'audio' && !selectedFormatId.value ? 'bestaudio' : null,
      media_type: mediaType.value,
      output_format: outputFormat.value === 'auto' ? null : outputFormat.value,
    })
    // Reset form on success
    url.value = ''
    formats.value = []
    showOptions.value = false
    selectedFormatId.value = null
    extractError.value = null
    mediaType.value = 'video'
    outputFormat.value = 'auto'
    urlInfo.value = null
    audioLocked.value = false
  } catch {
    // Error already in store.submitError
  }
}

function onFormatSelect(formatId: string | null): void {
  selectedFormatId.value = formatId
}

function handlePaste(): void {
  // Auto-extract on paste (populate formats + URL info silently in background)
  setTimeout(() => {
    if (url.value.trim()) {
      extractFormats()
      fetchUrlInfo()
    }
  }, 50)
}

async function fetchUrlInfo(): Promise<void> {
  const trimmed = url.value.trim()
  if (!trimmed) return
  isLoadingInfo.value = true
  urlInfo.value = null
  audioLocked.value = false
  try {
    const info = await api.getUrlInfo(trimmed)
    urlInfo.value = info
    // Auto-switch to audio if the source is audio-only
    if (info.is_audio_only) {
      mediaType.value = 'audio'
      audioLocked.value = true
    }
  } catch {
    // Non-critical — preview is optional
  } finally {
    isLoadingInfo.value = false
  }
}

function formatDuration(seconds: number | null): string {
  if (!seconds) return ''
  const m = Math.floor(seconds / 60)
  const s = Math.floor(seconds % 60)
  return `${m}:${s.toString().padStart(2, '0')}`
}

function toggleOptions(): void {
  showOptions.value = !showOptions.value
  // Extract formats when opening options if we haven't yet
  if (showOptions.value && !formatsReady.value && url.value.trim() && !isExtracting.value) {
    extractFormats()
  }
}

function formatLabel(fmt: string): string {
  if (fmt === 'auto') return 'Auto'
  return fmt.toUpperCase()
}

function formatTooltip(fmt: string): string {
  if (fmt !== 'auto') return `Convert to ${fmt.toUpperCase()}`
  if (mediaType.value === 'audio') {
    return 'Best quality audio in its native format (usually Opus/WebM)'
  }
  return 'Best quality video in its native format (usually WebM/MKV)'
}
</script>

<template>
  <div class="url-input">
    <!-- URL field -->
    <input
      v-model="url"
      type="url"
      placeholder="Paste a URL to download…"
      class="url-field"
      @paste="handlePaste"
      @keydown.enter="submitDownload"
      :disabled="isExtracting || store.isSubmitting"
    />

    <!-- Action row: gear, media toggle, download button -->
    <div class="action-row">
      <button
        class="btn-options"
        :class="{ active: showOptions }"
        @click="toggleOptions"
        :disabled="!url.trim()"
        title="Format options"
      >
        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
      </button>

      <div class="media-toggle">
        <button
          class="toggle-pill"
          :class="{ active: mediaType === 'video', disabled: audioLocked }"
          @click="!audioLocked && (mediaType = 'video')"
          :title="audioLocked ? 'Source contains audio only' : 'Video'"
          :disabled="audioLocked"
        >
          <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>
          <span class="toggle-label">Video</span>
        </button>
        <button
          class="toggle-pill"
          :class="{ active: mediaType === 'audio' }"
          @click="mediaType = 'audio'"
          :title="'Audio'"
        >
          <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18V5l12-2v13"/><circle cx="6" cy="18" r="3"/><circle cx="18" cy="16" r="3"/></svg>
          <span class="toggle-label">Audio</span>
        </button>
      </div>

      <button
        class="btn-download"
        @click="submitDownload"
        :disabled="!url.trim() || store.isSubmitting"
      >
        {{ store.isSubmitting ? 'Submitting…' : 'Download' }}
      </button>
    </div>

    <div v-if="isLoadingInfo" class="url-preview loading">
      <span class="spinner"></span>
      Checking URL…
    </div>

    <!-- URL preview: show what will be downloaded -->
    <div v-else-if="urlInfo" class="url-preview">
      <div class="preview-header">
        <span v-if="urlInfo.type === 'playlist'" class="preview-badge playlist">Playlist · {{ urlInfo.count }} items</span>
        <span v-else class="preview-badge single">Single {{ audioLocked ? 'track' : 'video' }}</span>
        <span v-if="audioLocked" class="preview-badge audio-only">Audio only</span>
        <span v-if="urlInfo.title" class="preview-title">{{ urlInfo.title }}</span>
      </div>
      <div v-if="urlInfo.type === 'playlist' && urlInfo.entries.length" class="preview-entries">
        <div
          v-for="(entry, i) in urlInfo.entries.slice(0, 10)"
          :key="i"
          class="preview-entry"
        >
          <span class="entry-num">{{ i + 1 }}.</span>
          <span class="entry-title">{{ entry.title }}</span>
          <span v-if="entry.duration" class="entry-duration">{{ formatDuration(entry.duration) }}</span>
        </div>
        <div v-if="urlInfo.entries.length > 10" class="preview-more">
          …and {{ urlInfo.entries.length - 10 }} more
        </div>
        <div v-if="urlInfo.unavailable_count" class="preview-warning">
          ⚠ {{ urlInfo.unavailable_count }} private/unavailable item{{ urlInfo.unavailable_count > 1 ? 's' : '' }} skipped
        </div>
      </div>
    </div>

    <div v-if="isExtracting" class="extract-loading">
      <span class="spinner"></span>
      Extracting available formats…
    </div>

    <div v-if="extractError" class="extract-error">
      {{ extractError }}
    </div>

    <div v-if="store.submitError" class="extract-error">
      {{ store.submitError }}
    </div>

    <!-- Collapsible options panel -->
    <Transition name="options-slide">
      <div v-if="showOptions" class="options-panel">
        <!-- Output format selector -->
        <div class="format-selector">
          <label class="format-label">Output format</label>
          <div class="format-chips">
            <button
              v-for="fmt in availableFormats"
              :key="fmt"
              class="format-chip"
              :class="{ active: outputFormat === fmt }"
              :title="formatTooltip(fmt)"
              @click="outputFormat = fmt"
            >
              {{ formatLabel(fmt) }}
            </button>
          </div>
        </div>

        <!-- Advanced format picker from yt-dlp extract_info -->
        <FormatPicker
          v-if="formatsReady"
          :formats="formats"
          @select="onFormatSelect"
        />
        <div v-else-if="!isExtracting" class="options-hint">
          Paste a URL and formats will load automatically.
        </div>
      </div>
    </Transition>
  </div>
</template>

<style scoped>
.url-input {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  width: 100%;
}

.url-field {
  width: 100%;
  font-size: var(--font-size-base);
}

/* Action row: gear | toggle | download */
.action-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}

.btn-options {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  padding: 0;
  flex-shrink: 0;
  background: var(--color-surface);
  color: var(--color-text-muted);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  transition: all 0.15s ease;
}

.btn-options:hover:not(:disabled) {
  color: var(--color-accent);
  border-color: var(--color-accent);
}

.btn-options.active {
  color: var(--color-accent);
  border-color: var(--color-accent);
  background: color-mix(in srgb, var(--color-accent) 10%, transparent);
}

.media-toggle {
  display: flex;
  gap: 0;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  overflow: hidden;
  flex-shrink: 0;
}

.toggle-pill {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--space-xs) var(--space-md);
  background: var(--color-surface);
  color: var(--color-text-muted);
  border: none;
  border-radius: 0;
  font-size: var(--font-size-sm);
  min-height: 38px;
  transition: all 0.15s ease;
}

.toggle-pill:not(:last-child) {
  border-right: 1px solid var(--color-border);
}

.toggle-pill:hover {
  background: var(--color-surface-hover);
  color: var(--color-text);
}

.toggle-pill.active {
  background: color-mix(in srgb, var(--color-accent) 15%, transparent);
  color: var(--color-accent);
}

.toggle-label {
  /* Visible by default, hidden at narrow widths */
}

.btn-download {
  flex: 1;
  white-space: nowrap;
  padding: var(--space-sm) var(--space-lg);
  font-weight: 600;
  min-height: 38px;
  background: var(--color-accent);
  color: var(--color-bg);
}

.btn-download:hover:not(:disabled) {
  background: var(--color-accent-hover);
}

button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Loading / errors */
.extract-loading {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  color: var(--color-text-muted);
  font-size: var(--font-size-sm);
  padding: var(--space-sm);
}

.spinner {
  display: inline-block;
  width: 16px;
  height: 16px;
  border: 2px solid var(--color-border);
  border-top-color: var(--color-accent);
  border-radius: 50%;
  animation: spin 0.6s linear infinite;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

.extract-error {
  color: var(--color-error);
  font-size: var(--font-size-sm);
  padding: var(--space-sm);
}

/* Options panel */
.options-panel {
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

.format-selector {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  padding: var(--space-sm) 0;
}

.format-label {
  font-size: var(--font-size-sm);
  color: var(--color-text-muted);
  white-space: nowrap;
  flex-shrink: 0;
}

.format-chips {
  display: flex;
  gap: var(--space-xs);
  flex-wrap: wrap;
}

.format-chip {
  padding: var(--space-xs) var(--space-md);
  font-size: var(--font-size-sm);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: var(--color-text-muted);
  cursor: pointer;
  transition: all 0.15s ease;
  min-height: 30px;
}

.format-chip:hover {
  color: var(--color-text);
  border-color: var(--color-text-muted);
}

.format-chip.active {
  background: color-mix(in srgb, var(--color-accent) 15%, transparent);
  color: var(--color-accent);
  border-color: var(--color-accent);
}

.options-slide-enter-active,
.options-slide-leave-active {
  transition: all 0.25s ease;
}

.options-slide-enter-from,
.options-slide-leave-to {
  opacity: 0;
  max-height: 0;
}

.options-slide-enter-to,
.options-slide-leave-from {
  opacity: 1;
  max-height: 400px;
}

.options-hint {
  padding: var(--space-md);
  text-align: center;
  color: var(--color-text-muted);
  font-size: var(--font-size-sm);
}

/* URL preview */
.url-preview {
  padding: var(--space-sm) var(--space-md);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  font-size: var(--font-size-sm);
}

.url-preview.loading {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  color: var(--color-text-muted);
}

.preview-header {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  flex-wrap: wrap;
}

.preview-badge {
  font-size: 11px;
  padding: 1px 8px;
  border-radius: 3px;
  font-weight: 600;
  text-transform: uppercase;
  white-space: nowrap;
}

.preview-badge.playlist {
  background: color-mix(in srgb, var(--color-accent) 15%, transparent);
  color: var(--color-accent);
}

.preview-badge.single {
  background: color-mix(in srgb, var(--color-text-muted) 15%, transparent);
  color: var(--color-text-muted);
}

.preview-badge.audio-only {
  background: color-mix(in srgb, var(--color-warning) 15%, transparent);
  color: var(--color-warning);
}

.preview-title {
  color: var(--color-text);
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.preview-entries {
  margin-top: var(--space-xs);
  display: flex;
  flex-direction: column;
  gap: 1px;
  max-height: 200px;
  overflow-y: auto;
}

.preview-entry {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: 2px 0;
  color: var(--color-text-muted);
}

.entry-num {
  min-width: 24px;
  text-align: right;
  color: var(--color-text-muted);
  opacity: 0.6;
}

.entry-title {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.entry-duration {
  font-family: var(--font-mono);
  color: var(--color-text-muted);
  opacity: 0.6;
}

.preview-more {
  padding: var(--space-xs) 0;
  color: var(--color-text-muted);
  font-style: italic;
}

.preview-warning {
  padding: var(--space-xs) var(--space-sm);
  margin-top: var(--space-xs);
  color: var(--color-warning);
  font-size: var(--font-size-sm);
  background: color-mix(in srgb, var(--color-warning) 10%, transparent);
  border-radius: var(--radius-sm);
}

.toggle-pill.disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* Narrow viewports: hide toggle labels, keep icons only */
@media (max-width: 540px) {
  .toggle-label {
    display: none;
  }

  .toggle-pill {
    padding: var(--space-xs) var(--space-sm);
  }
}

/* Mobile */
@media (max-width: 767px) {
  .btn-download {
    padding: var(--space-sm) var(--space-md);
  }
}
</style>
