<template>
  <div class="p-4">
    <section class="flex items-center gap-3">
      <Volume2
        class="size-5"
      />
      <div class="min-w-0">
        <h2 class="text-sm font-semibold truncate">
          {{ curProvider?.name }}
        </h2>
        <p class="text-xs text-muted-foreground">
          {{ currentMeta?.display_name ?? curProvider?.provider }}
        </p>
      </div>
      <div class="ml-auto flex items-center gap-2">
        <span class="text-xs text-muted-foreground">
          {{ $t('common.enable') }}
        </span>
        <Switch
          :model-value="curProvider?.enable ?? false"
          :disabled="!curProvider?.id || enableLoading"
          @update:model-value="handleToggleEnable"
        />
      </div>
    </section>
    <Separator class="mt-4 mb-6" />

    <form @submit="handleSave">
      <div class="space-y-5">
        <section>
          <FormField
            v-slot="{ componentField }"
            name="name"
          >
            <FormItem>
              <Label :for="componentField.id || 'tts-provider-name'">
                {{ $t('common.name') }}
              </Label>
              <FormControl>
                <Input
                  :id="componentField.id || 'tts-provider-name'"
                  type="text"
                  :placeholder="$t('common.namePlaceholder')"
                  v-bind="componentField"
                />
              </FormControl>
            </FormItem>
          </FormField>
        </section>

        <Separator class="my-4" />

        <!-- Models -->
        <section>
          <div class="flex justify-between items-center mb-4">
            <h3 class="text-xs font-medium">
              {{ $t('speech.models') }}
            </h3>
            <div
              v-if="curProviderId"
              class="flex items-center gap-2 ml-auto"
            >
              <LoadingButton
                type="button"
                variant="outline"
                class="flex items-center gap-2"
                :loading="importLoading"
                @click="handleImportModels"
              >
                <FileInput />
                {{ $t('speech.importModels') }}
              </LoadingButton>
              <AddTtsModel
                :provider-id="curProviderId"
                @created="refreshModels"
              />
            </div>
          </div>

          <div
            v-if="providerModels.length === 0"
            class="text-xs text-muted-foreground py-4 text-center"
          >
            {{ $t('speech.noModels') }}
          </div>

          <div
            v-for="model in providerModels"
            :key="model.id"
            class="border border-border rounded-lg mb-4"
          >
            <button
              type="button"
              class="w-full flex items-center justify-between p-3 text-left hover:bg-accent/50 rounded-t-lg transition-colors"
              @click="toggleModel(model.id)"
            >
              <div>
                <span class="text-xs font-medium">{{ model.name || model.model_id }}</span>
                <span
                  v-if="model.name"
                  class="text-xs text-muted-foreground ml-2"
                >{{ model.model_id }}</span>
              </div>
              <component
                :is="expandedModelId === model.id ? ChevronUp : ChevronDown"
                class="size-3 text-muted-foreground"
              />
            </button>

            <div
              v-if="expandedModelId === model.id"
              class="px-3 pb-3 space-y-4 border-t border-border pt-3"
            >
              <ModelConfigEditor
                :model-id="model.id"
                :model-name="model.model_id"
                :config="model.config || {}"
                :capabilities="getModelCapabilities(model.model_id)"
                @save="(cfg) => handleSaveModelConfig(model.id, cfg)"
                @test="(text, cfg) => handleTestModel(model.id, text, cfg)"
              />
            </div>
          </div>
        </section>
      </div>

      <section class="flex justify-end mt-6 gap-4">
        <ConfirmPopover
          :message="$t('speech.deleteConfirm')"
          :loading="deleteLoading"
          @confirm="handleDelete"
        >
          <template #trigger>
            <Button
              type="button"
              variant="outline"
            >
              <Trash2 />
            </Button>
          </template>
        </ConfirmPopover>
        <LoadingButton
          type="submit"
          :loading="editLoading"
        >
          {{ $t('provider.saveChanges') }}
        </LoadingButton>
      </section>
    </form>
  </div>
</template>

<script setup lang="ts">
import {
  Input,
  Button,
  FormControl,
  FormField,
  FormItem,
  Separator,
  Label,
  Switch,
} from '@memohai/ui'
import ConfirmPopover from '@/components/confirm-popover/index.vue'
import LoadingButton from '@/components/loading-button/index.vue'
import ModelConfigEditor from './model-config-editor.vue'
import { Volume2, FileInput, ChevronUp, ChevronDown, Trash2 } from 'lucide-vue-next'
import AddTtsModel from './add-tts-model.vue'
import { computed, inject, ref, watch } from 'vue'
import { toast } from 'vue-sonner'
import { useI18n } from 'vue-i18n'
import { toTypedSchema } from '@vee-validate/zod'
import z from 'zod'
import { useForm } from 'vee-validate'
import { useMutation, useQuery, useQueryCache } from '@pinia/colada'
import { putTtsProvidersById, deleteTtsProvidersById, getTtsProvidersMeta } from '@memohai/sdk'
import type { TtsProviderResponse, TtsProviderMetaResponse, TtsModelInfo } from '@memohai/sdk'

const { t } = useI18n()
const curProvider = inject('curTtsProvider', ref<TtsProviderResponse>())
const curProviderId = computed(() => curProvider.value?.id)
const enableLoading = ref(false)

const apiBase = import.meta.env.VITE_API_URL?.trim() || '/api'
function authHeaders(): Record<string, string> {
  const token = localStorage.getItem('token')
  return token ? { Authorization: `Bearer ${token}` } : {}
}

const { data: metaList } = useQuery({
  key: () => ['tts-providers-meta'],
  query: async () => {
    const { data } = await getTtsProvidersMeta({ throwOnError: true })
    return data
  },
})

const currentMeta = computed<TtsProviderMetaResponse | null>(() => {
  if (!metaList.value || !curProvider.value?.provider) return null
  return (metaList.value as TtsProviderMetaResponse[]).find((m) => m.provider === curProvider.value?.provider) ?? null
})

function getModelCapabilities(modelId: string) {
  const meta = currentMeta.value
  if (!meta?.models) return null
  return meta.models.find((m: TtsModelInfo) => m.id === modelId)?.capabilities ?? null
}

// Provider models
const { data: providerModelsData, refresh: refreshModels } = useQuery({
  key: () => ['tts-provider-models', curProviderId.value],
  query: async () => {
    if (!curProviderId.value) return []
    const resp = await fetch(`${apiBase}/tts-providers/${curProviderId.value}/models`, {
      headers: authHeaders(),
    })
    if (!resp.ok) throw new Error('Failed to fetch models')
    return resp.json()
  },
  enabled: () => !!curProviderId.value,
})

const providerModels = computed(() => providerModelsData.value ?? [])

const expandedModelId = ref('')
function toggleModel(id: string) {
  expandedModelId.value = expandedModelId.value === id ? '' : id
}

const queryCache = useQueryCache()

async function handleToggleEnable(value: boolean) {
  if (!curProviderId.value || !curProvider.value) return

  const prev = curProvider.value.enable ?? false
  curProvider.value = { ...curProvider.value, enable: value }

  enableLoading.value = true
  try {
    await putTtsProvidersById({
      path: { id: curProviderId.value },
      body: { enable: value },
      throwOnError: true,
    })
    queryCache.invalidateQueries({ key: ['tts-providers'] })
  } catch {
    curProvider.value = { ...curProvider.value, enable: prev }
    toast.error(t('common.saveFailed'))
  } finally {
    enableLoading.value = false
  }
}

const schema = toTypedSchema(z.object({
  name: z.string().min(1),
}))

const form = useForm({ validationSchema: schema })

let loadedProviderId = ''
watch(() => curProvider.value?.id, (id) => {
  if (!id || id === loadedProviderId) return
  loadedProviderId = id
  expandedModelId.value = ''
  const p = curProvider.value
  if (p) {
    form.setValues({ name: p.name ?? '' })
  }
}, { immediate: true })

const { mutateAsync: submitUpdate, isLoading: editLoading } = useMutation({
  mutation: async (data: { name: string }) => {
    if (!curProviderId.value) return
    const { data: result } = await putTtsProvidersById({
      path: { id: curProviderId.value },
      body: { name: data.name },
      throwOnError: true,
    })
    return result
  },
  onSettled: () => queryCache.invalidateQueries({ key: ['tts-providers'] }),
})

const { mutateAsync: doDelete, isLoading: deleteLoading } = useMutation({
  mutation: async () => {
    if (!curProviderId.value) return
    await deleteTtsProvidersById({ path: { id: curProviderId.value }, throwOnError: true })
  },
  onSettled: () => {
    queryCache.invalidateQueries({ key: ['tts-providers'] })
    queryCache.invalidateQueries({ key: ['tts-models'] })
  },
})

const handleSave = form.handleSubmit(async (values) => {
  try {
    await submitUpdate({ name: values.name })
    toast.success(t('provider.saveChanges'))
  } catch (e: unknown) {
    toast.error(e instanceof Error ? e.message : t('common.saveFailed'))
  }
})

async function handleDelete() {
  try {
    await doDelete()
    toast.success(t('common.deleteSuccess'))
  } catch (e: unknown) {
    toast.error(e instanceof Error ? e.message : t('common.saveFailed'))
  }
}

// Import models
const importLoading = ref(false)
async function handleImportModels() {
  if (!curProviderId.value) return
  importLoading.value = true
  try {
    const resp = await fetch(`${apiBase}/tts-providers/${curProviderId.value}/import-models`, {
      method: 'POST',
      headers: authHeaders(),
    })
    if (!resp.ok) throw new Error('Import failed')
    toast.success(t('speech.importSuccess'))
    refreshModels()
    queryCache.invalidateQueries({ key: ['tts-models'] })
  } catch (e: unknown) {
    toast.error(e instanceof Error ? e.message : t('speech.importFailed'))
  } finally {
    importLoading.value = false
  }
}

// Save model config
async function handleSaveModelConfig(modelId: string, config: Record<string, unknown>) {
  try {
    const resp = await fetch(`${apiBase}/tts-models/${modelId}`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json', ...authHeaders() },
      body: JSON.stringify({ config }),
    })
    if (!resp.ok) throw new Error('Save failed')
    toast.success(t('provider.saveChanges'))
    refreshModels()
    queryCache.invalidateQueries({ key: ['tts-models'] })
  } catch (e: unknown) {
    toast.error(e instanceof Error ? e.message : t('common.saveFailed'))
  }
}

// Test model synthesis
async function handleTestModel(modelId: string, text: string, config: Record<string, unknown>) {
  const resp = await fetch(`${apiBase}/tts-models/${modelId}/test`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', ...authHeaders() },
    body: JSON.stringify({ text, config }),
  })
  if (!resp.ok) {
    const errBody = await resp.text()
    let msg: string
    try {
      msg = JSON.parse(errBody)?.message ?? errBody
    } catch {
      msg = errBody
    }
    throw new Error(msg)
  }
  return resp.blob()
}
</script>
