<script setup>
import { ref, computed, onMounted, watch } from 'vue'
import { useTranslation } from 'i18next-vue'
import { useRouter, useRoute } from 'vue-router'
import { useExercises } from '../composables/useExercises.js'
import { useSettings } from '../composables/useSettings.js'
import { validateRequired, validateMin, validateUrl } from '../utils/validation.js'
import { useKeyboardShortcut } from '../composables/useKeyboardShortcut.js'

const { t } = useTranslation()

function te(err) {
  if (!err) return ''
  if (typeof err === 'object') return t(err.key, err.params)
  return t(err)
}
const router = useRouter()
const route = useRoute()
const { create, update, getById } = useExercises()
const { get: getSetting } = useSettings()

const isEdit = computed(() => route.name === 'exercise-edit')
const exerciseId = computed(() => route.params.id)

// Form fields
const title = ref('')
const description = ref('')
const asymmetric = ref(false)
const alternate = ref(false)
const imageUrls = ref([])
const videoUrls = ref([])
const defaultReps = ref(0)
const defaultWeight = ref(0)

// Validation errors
const errors = ref({})
const saving = ref(false)

// New URL inputs
const newImageUrl = ref('')
const newVideoUrl = ref('')

onMounted(async () => {
  if (isEdit.value && exerciseId.value) {
    const exercise = await getById(exerciseId.value)
    if (!exercise) {
      router.replace({ name: 'exercises' })
      return
    }
    title.value = exercise.title
    description.value = exercise.description || ''
    asymmetric.value = exercise.asymmetric
    alternate.value = exercise.alternate
    imageUrls.value = [...(exercise.image_urls || [])]
    videoUrls.value = [...(exercise.video_urls || [])]
    defaultReps.value = exercise.default_reps || 0
    defaultWeight.value = exercise.default_weight || 0
  }
})

watch(asymmetric, (val) => {
  if (val && defaultReps.value > 0 && defaultReps.value % 2 !== 0) {
    defaultReps.value++
  }
})

function onDefaultRepsChange() {
  if (asymmetric.value && defaultReps.value > 0 && defaultReps.value % 2 !== 0) {
    defaultReps.value++
  }
}

function validate() {
  const e = {}
  const titleErr = validateRequired(title.value)
  if (titleErr) e.title = titleErr

  const repsErr = validateMin(defaultReps.value, 0)
  if (repsErr) e.defaultReps = repsErr

  const weightErr = validateMin(defaultWeight.value, 0)
  if (weightErr) e.defaultWeight = weightErr

  // Validate existing URLs in lists
  for (let i = 0; i < imageUrls.value.length; i++) {
    const urlErr = validateUrl(imageUrls.value[i])
    if (urlErr) {
      e[`imageUrl_${i}`] = urlErr
    }
  }
  for (let i = 0; i < videoUrls.value.length; i++) {
    const urlErr = validateUrl(videoUrls.value[i])
    if (urlErr) {
      e[`videoUrl_${i}`] = urlErr
    }
  }

  errors.value = e
  return Object.keys(e).length === 0
}

function addImageUrl() {
  const urlErr = validateUrl(newImageUrl.value)
  if (urlErr) {
    errors.value = { ...errors.value, newImageUrl: urlErr }
    return
  }
  if (!newImageUrl.value.trim()) return
  imageUrls.value.push(newImageUrl.value.trim())
  newImageUrl.value = ''
  // Clear any previous error on this field
  const { newImageUrl: _, ...rest } = errors.value
  errors.value = rest
}

function removeImageUrl(index) {
  imageUrls.value.splice(index, 1)
}

function addVideoUrl() {
  const urlErr = validateUrl(newVideoUrl.value)
  if (urlErr) {
    errors.value = { ...errors.value, newVideoUrl: urlErr }
    return
  }
  if (!newVideoUrl.value.trim()) return
  videoUrls.value.push(newVideoUrl.value.trim())
  newVideoUrl.value = ''
  const { newVideoUrl: _, ...rest } = errors.value
  errors.value = rest
}

function removeVideoUrl(index) {
  videoUrls.value.splice(index, 1)
}

async function onSave() {
  if (!validate()) return

  saving.value = true
  try {
    const userUuid = await getSetting('user_uuid')
    const data = {
      title: title.value.trim(),
      description: description.value.trim(),
      asymmetric: asymmetric.value,
      alternate: asymmetric.value ? alternate.value : false,
      image_urls: imageUrls.value,
      video_urls: videoUrls.value,
      default_reps: Number(defaultReps.value) || 0,
      default_weight: Number(defaultWeight.value) || 0,
      created_by: userUuid,
    }

    if (isEdit.value) {
      await update(exerciseId.value, data)
      router.push({ name: 'exercise-detail', params: { id: exerciseId.value } })
    } else {
      const id = await create(data)
      router.push({ name: 'exercise-detail', params: { id } })
    }
  } catch (e) {
    // Handle unique constraint violation on title
    if (e.message && e.message.includes('UNIQUE')) {
      errors.value = { ...errors.value, title: 'validation.titleTaken' }
    } else {
      errors.value = { ...errors.value, save: e.message }
    }
  } finally {
    saving.value = false
  }
}

useKeyboardShortcut('enter', onSave, { ctrl: true, ignoreInputs: false })
useKeyboardShortcut('escape', onCancel, { ignoreInputs: false })

function onCancel() {
  if (isEdit.value) {
    router.push({ name: 'exercise-detail', params: { id: exerciseId.value } })
  } else {
    router.push({ name: 'exercises' })
  }
}
</script>

<template>
  <Teleport to="#actionbar-actions" defer>
    <button
      data-testid="exercise-form-cancel"
      :title="$t('exercises.form.cancel')"
      :aria-label="$t('exercises.form.cancel')"
      @click="onCancel"
    >
      <i class="bi bi-x-lg"></i>
    </button>
  </Teleport>

  <div class="exercise-form-view">
    <h2 data-testid="exercise-form-heading">
      {{ isEdit ? $t('exercises.edit') : $t('exercises.new') }}
    </h2>

    <form novalidate @submit.prevent="onSave">
      <!-- Title -->
      <div class="form-group">
        <label for="exercise-title">{{ $t('exercises.form.title') }} *</label>
        <input
          id="exercise-title"
          v-model="title"
          data-testid="exercise-title"
          type="text"
          :placeholder="$t('exercises.form.titlePlaceholder')"
          :class="{ 'input-error': errors.title }"
          required
          aria-required="true"
          :aria-describedby="errors.title ? 'exercise-title-error' : undefined"
        />
        <p
          v-if="errors.title"
          id="exercise-title-error"
          role="alert"
          class="field-error"
          data-testid="exercise-title-error"
        >
          {{ $t(errors.title) }}
        </p>
      </div>

      <!-- Description -->
      <div class="form-group">
        <label for="exercise-description">
          {{ $t('exercises.form.description') }}
          <span class="field-hint">{{ $t('common.markdownHint') }}</span>
        </label>
        <textarea
          id="exercise-description"
          v-model="description"
          data-testid="exercise-description"
          :placeholder="$t('exercises.form.descriptionPlaceholder')"
          rows="3"
        ></textarea>
      </div>

      <!-- Asymmetric -->
      <div class="form-group">
        <label class="checkbox-label">
          <input v-model="asymmetric" type="checkbox" data-testid="exercise-asymmetric" />
          {{ $t('exercises.form.asymmetric') }}
        </label>
      </div>

      <!-- Alternate (only visible when asymmetric) -->
      <div v-if="asymmetric" class="form-group form-sub-group">
        <label class="radio-label">
          <input
            v-model="alternate"
            type="radio"
            name="alternate"
            :value="true"
            data-testid="exercise-alternate-true"
          />
          {{ $t('exercises.form.alternate') }}
        </label>
        <label class="radio-label">
          <input
            v-model="alternate"
            type="radio"
            name="alternate"
            :value="false"
            data-testid="exercise-alternate-false"
          />
          {{ $t('exercises.form.alternateFalse') }}
        </label>
      </div>

      <!-- Default Reps -->
      <div class="form-group">
        <label for="exercise-reps">{{ $t('exercises.form.defaultReps') }}</label>
        <div class="stepper">
          <button
            type="button"
            class="stepper-btn btn-primary"
            :aria-label="'-1 ' + $t('exercises.form.defaultReps')"
            @click="defaultReps = Math.max(0, defaultReps - (asymmetric ? 2 : 1))"
          >
            <i class="bi bi-dash-circle-fill"></i>
          </button>
          <input
            id="exercise-reps"
            v-model.number="defaultReps"
            data-testid="exercise-reps"
            type="number"
            min="0"
            :step="asymmetric ? 2 : 1"
            class="step-input"
            :class="{ 'input-error': errors.defaultReps }"
            :aria-describedby="errors.defaultReps ? 'exercise-reps-error' : undefined"
            @change="onDefaultRepsChange"
          />
          <button
            type="button"
            class="stepper-btn btn-primary"
            :aria-label="'+1 ' + $t('exercises.form.defaultReps')"
            @click="defaultReps += asymmetric ? 2 : 1"
          >
            <i class="bi bi-plus-circle-fill"></i>
          </button>
          <button
            type="button"
            class="stepper-btn stepper-btn-fast"
            :aria-label="'+5 ' + $t('exercises.form.defaultReps')"
            @click="defaultReps += asymmetric ? 10 : 5"
          >
            <i class="bi bi-forward-fill"></i>
          </button>
        </div>
        <p
          v-if="errors.defaultReps"
          id="exercise-reps-error"
          role="alert"
          class="field-error"
          data-testid="exercise-reps-error"
        >
          {{ te(errors.defaultReps) }}
        </p>
        <p v-if="asymmetric" class="field-hint">{{ $t('exercises.evenRepsHint') }}</p>
      </div>

      <!-- Default Weight -->
      <div class="form-group">
        <label for="exercise-weight">{{ $t('exercises.form.defaultWeight') }}</label>
        <div class="stepper">
          <button
            type="button"
            class="stepper-btn btn-primary"
            :aria-label="'-0.5 ' + $t('exercises.form.defaultWeight')"
            @click="defaultWeight = Math.max(0, +(defaultWeight - 0.5).toFixed(1))"
          >
            <i class="bi bi-dash-circle-fill"></i>
          </button>
          <input
            id="exercise-weight"
            v-model.number="defaultWeight"
            data-testid="exercise-weight"
            type="number"
            min="0"
            step="0.5"
            class="step-input"
            :class="{ 'input-error': errors.defaultWeight }"
            :aria-describedby="errors.defaultWeight ? 'exercise-weight-error' : undefined"
          />
          <button
            type="button"
            class="stepper-btn btn-primary"
            :aria-label="'+0.5 ' + $t('exercises.form.defaultWeight')"
            @click="defaultWeight = +(defaultWeight + 0.5).toFixed(1)"
          >
            <i class="bi bi-plus-circle-fill"></i>
          </button>
          <button
            type="button"
            class="stepper-btn stepper-btn-fast"
            :aria-label="'+1 ' + $t('exercises.form.defaultWeight')"
            @click="defaultWeight = +(defaultWeight + 1).toFixed(1)"
          >
            <i class="bi bi-forward-fill"></i>
          </button>
        </div>
        <p
          v-if="errors.defaultWeight"
          id="exercise-weight-error"
          role="alert"
          class="field-error"
          data-testid="exercise-weight-error"
        >
          {{ te(errors.defaultWeight) }}
        </p>
      </div>

      <!-- Image URLs -->
      <div class="form-group">
        <label>{{ $t('exercises.form.imageUrls') }}</label>
        <ul v-if="imageUrls.length" class="url-list">
          <li v-for="(url, i) in imageUrls" :key="'img-' + i" class="url-item">
            <span class="url-text">{{ url }}</span>
            <button
              type="button"
              class="btn-remove"
              :data-testid="'exercise-remove-image-' + i"
              @click="removeImageUrl(i)"
            >
              <i class="bi bi-trash"></i>
            </button>
            <p v-if="errors['imageUrl_' + i]" class="field-error">
              {{ $t(errors['imageUrl_' + i]) }}
            </p>
          </li>
        </ul>
        <div class="url-add">
          <input
            v-model="newImageUrl"
            type="url"
            :placeholder="$t('exercises.form.urlPlaceholder')"
            data-testid="exercise-new-image-url"
            :class="{ 'input-error': errors.newImageUrl }"
            @keyup.enter.prevent="addImageUrl"
          />
          <button
            type="button"
            class="btn btn-primary btn-sm"
            data-testid="exercise-add-image-url"
            @click="addImageUrl"
          >
            <i class="bi bi-plus"></i> {{ $t('exercises.form.addUrl') }}
          </button>
        </div>
        <p v-if="errors.newImageUrl" class="field-error" data-testid="exercise-image-url-error">
          {{ $t(errors.newImageUrl) }}
        </p>
      </div>

      <!-- Video URLs -->
      <div class="form-group">
        <label>{{ $t('exercises.form.videoUrls') }}</label>
        <ul v-if="videoUrls.length" class="url-list">
          <li v-for="(url, i) in videoUrls" :key="'vid-' + i" class="url-item">
            <span class="url-text">{{ url }}</span>
            <button
              type="button"
              class="btn-remove"
              :data-testid="'exercise-remove-video-' + i"
              @click="removeVideoUrl(i)"
            >
              <i class="bi bi-trash"></i>
            </button>
            <p v-if="errors['videoUrl_' + i]" class="field-error">
              {{ $t(errors['videoUrl_' + i]) }}
            </p>
          </li>
        </ul>
        <div class="url-add">
          <input
            v-model="newVideoUrl"
            type="url"
            :placeholder="$t('exercises.form.urlPlaceholder')"
            data-testid="exercise-new-video-url"
            :class="{ 'input-error': errors.newVideoUrl }"
            @keyup.enter.prevent="addVideoUrl"
          />
          <button
            type="button"
            class="btn btn-primary btn-sm"
            data-testid="exercise-add-video-url"
            @click="addVideoUrl"
          >
            <i class="bi bi-plus"></i> {{ $t('exercises.form.addUrl') }}
          </button>
        </div>
        <p v-if="errors.newVideoUrl" class="field-error" data-testid="exercise-video-url-error">
          {{ $t(errors.newVideoUrl) }}
        </p>
      </div>

      <!-- Save error -->
      <p v-if="errors.save" class="field-error" data-testid="exercise-save-error">
        {{ errors.save }}
      </p>

      <!-- Actions -->
      <div class="form-actions">
        <button
          type="submit"
          class="btn btn-primary"
          data-testid="exercise-save"
          :disabled="saving"
          :title="$t('exercises.form.save') + ' ' + $t('shortcuts.save')"
        >
          <i class="bi bi-check-lg"></i>
          {{ $t('exercises.form.save') }}
        </button>
        <button type="button" class="btn" data-testid="exercise-cancel" @click="onCancel">
          {{ $t('exercises.form.cancel') }}
        </button>
      </div>
    </form>
  </div>
</template>

<style scoped>
.exercise-form-view {
  max-width: 600px;
}

.exercise-form-view h2 {
  margin-bottom: 16px;
  font-size: 20px;
}

.form-group {
  margin-bottom: 16px;
}

.form-group label {
  display: flex;
  align-items: baseline;
  gap: 6px;
  font-weight: 600;
  font-size: 14px;
  margin-bottom: 4px;
}

.field-hint {
  font-weight: 400;
  font-size: 11px;
  opacity: 0.55;
}

.form-sub-group {
  padding-left: 24px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.checkbox-label,
.radio-label {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 400;
  cursor: pointer;
}

.checkbox-label input,
.radio-label input {
  width: auto;
  margin: 0;
}

.input-error {
  border-color: var(--btn-danger-bg) !important;
}

.stepper {
  display: flex;
  align-items: center;
  gap: 4px;
}

.stepper-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  font-size: 22px;
  flex-shrink: 0;
  transition: opacity 0.15s;
}

.stepper-btn:hover {
  opacity: 0.8;
}

.stepper-btn:active {
  opacity: 0.6;
}

.stepper-btn-fast {
  background: var(--btn-primary-bg);
  color: var(--btn-primary-color);
}

.step-input {
  width: 64px;
  padding: 6px 4px;
  border: 1px solid var(--border-color);
  border-radius: 6px;
  background: var(--color-background);
  color: var(--color-text);
  font-size: 18px;
  font-weight: 600;
  text-align: center;
}

.field-error {
  color: var(--btn-danger-bg);
  font-size: 13px;
  margin: 4px 0 0;
}

.field-hint {
  color: var(--color-text-muted);
  font-size: 12px;
  margin: 4px 0 0;
}

.url-list {
  list-style: none;
  padding: 0;
  margin: 0 0 8px;
}

.url-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 0;
  border-bottom: 1px solid var(--border-color);
}

.url-text {
  flex: 1;
  font-size: 13px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.btn-remove {
  background: none;
  border: none;
  color: var(--btn-danger-bg);
  cursor: pointer;
  padding: 4px;
  font-size: 14px;
  display: flex;
  align-items: center;
}

.btn-remove:hover {
  opacity: 0.7;
}

.url-add {
  display: flex;
  gap: 8px;
  align-items: flex-start;
}

.url-add input {
  flex: 1;
}

.btn-sm {
  padding: 6px 12px;
  font-size: 13px;
  white-space: nowrap;
}

.form-actions {
  display: flex;
  gap: 8px;
  margin-top: 24px;
}

@media screen and (max-width: 400px) {
  .url-add {
    flex-wrap: wrap;
  }

  .url-add input {
    flex: 1 1 100%;
  }

  .form-actions {
    flex-wrap: wrap;
  }

  .form-actions .btn {
    flex: 1 1 auto;
    justify-content: center;
  }
}
</style>
