<template>
  <div class="view">
    <EntityEditForm
      entityLabel="Routine"
      :fields="fields"
      :initialData="initialData"
      :isEdit="isEdit"
      :loading="loading"
      :error="error"
      :requireDirty="false"
      :submitDisabled="items.length === 0"
      @submit="handleSubmit"
      @cancel="handleCancel"
      @add-image="handleAddMainImage"
    >
      <template #before-actions>
        <section class="items-panel">
          <h3>Routine Tasks</h3>

          <div v-if="items.length === 0" class="items-empty">
            Add at least one task to save this routine.
          </div>

          <div v-else class="items-list">
            <div
              v-for="(item, idx) in items"
              :key="item.id || `new-${idx}`"
              class="item-row"
              :data-idx="idx"
              :class="{ 'drag-over': dragOverIdx === idx, dragging: draggingIdx === idx }"
            >
              <span class="drag-handle" data-tutorial="routine-task-reorder" title="Drag to reorder" @pointerdown.prevent="(e) => onPointerDown(e, idx)">⠿</span>
              <div class="item-left">
                <img
                  v-if="item.image_url"
                  :src="item.image_url"
                  :alt="item.name"
                  class="item-thumb"
                />
                <span class="item-name">{{ item.name }}</span>
              </div>
              <div class="item-actions">
                <button
                  type="button"
                  class="btn btn-secondary small-btn"
                  data-tutorial="routine-task-edit"
                  @click="startEditItem(idx)"
                >
                  Edit
                </button>
                <button
                  type="button"
                  class="btn btn-secondary small-btn"
                  data-tutorial="routine-task-delete"
                  @click="removeItem(idx)"
                >
                  Delete
                </button>
              </div>
            </div>
          </div>

          <button v-if="!addFormOpen" type="button" class="add-task-trigger" @click="openAddForm">
            <span class="add-task-plus">＋</span> Add Task
          </button>

          <div v-else class="add-item-form">
            <div class="add-form-header">
              <h4>{{ editingItemIdx !== null ? 'Edit Task' : 'New Task' }}</h4>
              <button
                type="button"
                class="close-form-btn"
                @click="cancelEditItem"
                aria-label="Close"
              >
                ✕
              </button>
            </div>

            <div class="group">
              <input
                id="item-name"
                v-model="newItem.name"
                type="text"
                maxlength="64"
                placeholder="Task name, e.g. Make bed"
                @input="error = null"
                autofocus
              />
            </div>

            <div v-if="showImagePicker" class="group">
              <ImagePicker
                id="item-image"
                v-model="newItem.image_id"
                :image-type="2"
                @add-image="handleAddItemImage"
              />
            </div>
            <button v-else type="button" class="toggle-image-btn" @click="showImagePicker = true">
              + Add image (optional)
            </button>

            <div class="item-form-actions">
              <button
                type="button"
                class="btn btn-primary"
                @click="addOrUpdateItem"
                :disabled="!newItem.name.trim()"
              >
                {{ editingItemIdx !== null ? 'Update' : 'Add' }}
              </button>
              <button type="button" class="btn btn-secondary" @click="cancelEditItem">
                Cancel
              </button>
            </div>
          </div>
        </section>
      </template>
    </EntityEditForm>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, computed } from 'vue'
import { useRouter } from 'vue-router'
import EntityEditForm from '@/components/shared/EntityEditForm.vue'
import ImagePicker from '@/components/utils/ImagePicker.vue'
import { getCachedImageUrl } from '@/common/imageCache'
import type { RoutineItem } from '@/common/models'
import { maybeShow as tutorialMaybeShow } from '@/tutorial/controller'
import '@/assets/styles.css'

const props = defineProps<{ id?: string }>()
const router = useRouter()
const isEdit = computed(() => !!props.id)

type EditableRoutineItem = RoutineItem & {
  image_url?: string | null
  local_file?: File | null
}

const fields = [
  { name: 'name', label: 'Routine Name', type: 'text' as const, required: true, maxlength: 64 },
  { name: 'points', label: 'Points', type: 'number' as const, required: true, min: 1, max: 1000 },
  { name: 'image_id', label: 'Image', type: 'image' as const, imageType: 2 },
]

const initialData = ref({
  name: '',
  points: 1,
  image_id: null as string | null,
})
const items = ref<EditableRoutineItem[]>([])
const newItem = ref({
  name: '',
  image_id: null as string | null,
})
const newItemLocalFile = ref<File | null>(null)
const editingItemIdx = ref<number | null>(null)
const addFormOpen = ref(false)
const showImagePicker = ref(false)
const loading = ref(false)
const error = ref<string | null>(null)
const localImageFile = ref<File | null>(null)
const removedItemIds = ref<string[]>([])
const draggingIdx = ref<number | null>(null)
const dragOverIdx = ref<number | null>(null)

onMounted(async () => {
  if (!isEdit.value) tutorialMaybeShow('edit-routine-name')
  if (isEdit.value && props.id) {
    loading.value = true
    try {
      const resp = await fetch(`/api/routine/${props.id}`)
      if (!resp.ok) throw new Error('Failed to load routine')
      const data = await resp.json()
      initialData.value = {
        name: data.name ?? '',
        points: Number(data.points) || 1,
        image_id: data.image_id ?? null,
      }

      const itemsResp = await fetch(`/api/routine/${props.id}/items`)
      if (itemsResp.ok) {
        const itemsData = await itemsResp.json()
        const loadedItems = (itemsData.items || []) as RoutineItem[]
        items.value = await Promise.all(
          loadedItems.map(async (item, idx) => {
            let imageUrl: string | null = null
            if (item.image_id) {
              try {
                imageUrl = await getCachedImageUrl(item.image_id)
              } catch {
                imageUrl = null
              }
            }
            return {
              ...item,
              order: idx,
              image_url: imageUrl,
              local_file: null,
            }
          }),
        )
      }
    } catch {
      error.value = 'Could not load routine.'
    } finally {
      loading.value = false
    }
  }
})

function handleAddMainImage({ id, file }: { id: string; file: File }) {
  if (id === 'local-upload') {
    localImageFile.value = file
  } else {
    localImageFile.value = null
  }
}

async function handleAddItemImage({ id, file }: { id: string; file: File }) {
  if (id !== 'local-upload') {
    newItemLocalFile.value = null
    return
  }

  newItemLocalFile.value = file
  try {
    const imageUrl = await getCachedImageUrl(id)
    if (editingItemIdx.value !== null) {
      const editingItem = items.value[editingItemIdx.value]
      if (editingItem) {
        editingItem.image_url = imageUrl
      }
    }
  } catch {
    // No-op: preview still works through ImagePicker.
  }
}

function openAddForm() {
  editingItemIdx.value = null
  newItem.value = { name: '', image_id: null }
  newItemLocalFile.value = null
  showImagePicker.value = false
  addFormOpen.value = true
}

function startEditItem(idx: number) {
  const item = items.value[idx]
  if (!item) return

  editingItemIdx.value = idx
  newItem.value = {
    name: item.name,
    image_id: item.image_id,
  }
  newItemLocalFile.value = null
  showImagePicker.value = !!item.image_id
  addFormOpen.value = true
}

function normalizeItemOrder() {
  items.value = items.value.map((item, idx) => ({
    ...item,
    order: idx,
  }))
}

function addOrUpdateItem() {
  if (!newItem.value.name.trim()) {
    error.value = 'Item name is required.'
    return
  }

  if (editingItemIdx.value !== null) {
    const current = items.value[editingItemIdx.value]
    if (!current) {
      cancelEditItem()
      return
    }

    items.value[editingItemIdx.value] = {
      id: current.id || '',
      routine_id: props.id || '',
      name: newItem.value.name,
      image_id: newItem.value.image_id,
      image_url: current.image_url,
      local_file: newItemLocalFile.value,
      order: editingItemIdx.value,
    }
  } else {
    items.value.push({
      id: `temp-${Date.now()}`,
      routine_id: props.id || '',
      name: newItem.value.name,
      image_id: newItem.value.image_id,
      image_url: null,
      local_file: newItemLocalFile.value,
      order: items.value.length,
    })
  }

  normalizeItemOrder()
  editingItemIdx.value = null
  addFormOpen.value = false
  showImagePicker.value = false
  newItem.value = { name: '', image_id: null }
  newItemLocalFile.value = null
  error.value = null
}

function removeItem(idx: number) {
  const item = items.value[idx]
  if (!item) return

  if (item.id && !item.id.startsWith('temp-')) {
    removedItemIds.value.push(item.id)
  }
  items.value.splice(idx, 1)
  normalizeItemOrder()

  if (editingItemIdx.value === idx) {
    cancelEditItem()
  } else if (editingItemIdx.value !== null && editingItemIdx.value > idx) {
    editingItemIdx.value -= 1
  }
}

function cancelEditItem() {
  editingItemIdx.value = null
  addFormOpen.value = false
  showImagePicker.value = false
  newItem.value = { name: '', image_id: null }
  newItemLocalFile.value = null
}

function onPointerDown(e: PointerEvent, idx: number) {
  draggingIdx.value = idx
  cancelEditItem()

  const handlePointerMove = (moveEvt: PointerEvent) => {
    moveEvt.preventDefault()
    const el = document.elementFromPoint(moveEvt.clientX, moveEvt.clientY) as Element | null
    if (!el) return
    const row = el.closest('[data-idx]') as HTMLElement | null
    if (!row) return
    const rowIdx = parseInt(row.dataset.idx ?? '-1')
    if (rowIdx !== -1 && rowIdx !== draggingIdx.value) {
      dragOverIdx.value = rowIdx
    }
  }

  const cleanup = (upEvt?: PointerEvent) => {
    if (upEvt) {
      const el = document.elementFromPoint(upEvt.clientX, upEvt.clientY) as Element | null
      const fromIdx = draggingIdx.value
      if (el && fromIdx !== null) {
        const row = el.closest('[data-idx]') as HTMLElement | null
        if (row) {
          const toIdx = parseInt(row.dataset.idx ?? '-1')
          if (toIdx !== -1 && toIdx !== fromIdx) {
            const moved = items.value.splice(fromIdx, 1)[0]
            items.value.splice(toIdx, 0, moved)
            normalizeItemOrder()
          }
        }
      }
    }
    draggingIdx.value = null
    dragOverIdx.value = null
    document.removeEventListener('pointermove', handlePointerMove)
    document.removeEventListener('pointerup', handlePointerUp)
    document.removeEventListener('pointercancel', handlePointerCancel)
  }

  const handlePointerUp = (upEvt: PointerEvent) => cleanup(upEvt)
  const handlePointerCancel = () => cleanup()

  document.addEventListener('pointermove', handlePointerMove, { passive: false })
  document.addEventListener('pointerup', handlePointerUp)
  document.addEventListener('pointercancel', handlePointerCancel)
}

async function uploadImage(file: File): Promise<string> {
  const formData = new FormData()
  formData.append('file', file)
  formData.append('type', '2')
  formData.append('permanent', 'false')
  const resp = await fetch('/api/image/upload', { method: 'POST', body: formData })
  if (!resp.ok) throw new Error('Image upload failed')
  const data = await resp.json()
  return data.id
}

async function resolveItemImageId(item: EditableRoutineItem): Promise<string | null> {
  if (item.image_id === 'local-upload' && item.local_file) {
    return uploadImage(item.local_file)
  }
  return item.image_id ?? null
}

async function handleSubmit(form: { name: string; points: number; image_id: string | null }) {
  error.value = null
  if (!form.name.trim()) {
    error.value = 'Routine name is required.'
    return
  }
  if (form.points < 1) {
    error.value = 'Points must be at least 1.'
    return
  }
  if (items.value.length === 0) {
    error.value = 'Routine must have at least one item.'
    return
  }

  loading.value = true

  try {
    let imageId = form.image_id
    if (imageId === null && localImageFile.value) {
      imageId = await uploadImage(localImageFile.value)
    }
    if (imageId === 'local-upload' && localImageFile.value) {
      imageId = await uploadImage(localImageFile.value)
    }

    const url = isEdit.value && props.id ? `/api/routine/${props.id}/edit` : '/api/routine/add'
    const resp = await fetch(url, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ name: form.name, points: form.points, image_id: imageId }),
    })
    if (!resp.ok) throw new Error('Failed to save routine')
    const routineData = await resp.json()
    const routineId =
      props.id ||
      (typeof routineData?.id === 'string' && routineData.id) ||
      (typeof routineData?.routine?.id === 'string' && routineData.routine.id) ||
      ''

    if (!routineId) {
      throw new Error('Failed to resolve routine id after save')
    }

    for (const itemId of removedItemIds.value) {
      const deleteResp = await fetch(`/api/routine/${routineId}/item/${itemId}`, {
        method: 'DELETE',
      })
      if (!deleteResp.ok) {
        throw new Error('Failed to delete routine item')
      }
    }

    for (const item of items.value) {
      const resolvedImageId = await resolveItemImageId(item)
      const payload = { name: item.name, image_id: resolvedImageId, order: item.order }

      if (item.id?.startsWith('temp-') || !item.id) {
        const addItemResp = await fetch(`/api/routine/${routineId}/item/add`, {
          method: 'PUT',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(payload),
        })
        if (!addItemResp.ok) {
          throw new Error('Failed to add routine item')
        }
      } else {
        const editItemResp = await fetch(`/api/routine/${routineId}/item/${item.id}/edit`, {
          method: 'PUT',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(payload),
        })
        if (!editItemResp.ok) {
          throw new Error('Failed to edit routine item')
        }
      }
    }

    await router.push({ name: 'RoutineView' })
  } catch (err) {
    error.value = 'Failed to save routine.'
    console.error(err)
  } finally {
    loading.value = false
  }
}

function handleCancel() {
  router.back()
}
</script>

<style scoped>
.view {
  max-width: 400px;
  margin: 0 auto;
  background: var(--form-bg);
  border-radius: 12px;
  box-shadow: 0 4px 24px var(--form-shadow);
  padding: 2rem 2.2rem 1.5rem 2.2rem;
}

.items-panel {
  margin-top: 1rem;
  border-top: 1px solid var(--form-input-border, #e6e6e6);
  padding-top: 1rem;
}

.items-panel h3 {
  color: var(--form-label, #444);
  font-size: 1.1rem;
  margin: 0 0 0.75rem;
}

.items-empty {
  color: var(--loading-color, #888);
  font-size: 0.95rem;
  margin-bottom: 0.75rem;
}

.items-list {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  margin-bottom: 1rem;
}

.item-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: var(--form-input-bg, #f8fafc);
  border: 1px solid var(--form-input-border, #e6e6e6);
  border-radius: 10px;
  padding: 0.55rem 0.7rem;
  cursor: default;
}

.item-row.drag-over {
  border-color: var(--btn-primary, #667eea);
  background: var(--form-input-bg-hover, #f0f4ff);
}

.item-row.dragging {
  opacity: 0.4;
}

.drag-handle {
  cursor: grab;
  color: var(--form-label, #aaa);
  font-size: 1.15rem;
  padding: 0.4rem 0.45rem 0.4rem 0;
  user-select: none;
  touch-action: none;
  flex-shrink: 0;
}

.drag-handle:active {
  cursor: grabbing;
}

.item-left {
  display: flex;
  align-items: center;
  min-width: 0;
  gap: 0.6rem;
  flex: 1;
}

.item-thumb {
  width: 34px;
  height: 34px;
  border-radius: 8px;
  object-fit: cover;
}

.item-name {
  font-weight: 600;
  color: var(--form-label, #333);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.item-actions {
  display: flex;
  gap: 0.5rem;
}

.add-task-trigger {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  width: 100%;
  padding: 0.6rem 0.9rem;
  background: none;
  border: 1.5px dashed var(--form-input-border, #c8d0db);
  border-radius: 10px;
  color: var(--btn-primary, #667eea);
  font-size: 0.95rem;
  font-weight: 600;
  cursor: pointer;
  transition:
    background 0.15s,
    border-color 0.15s;
}

.add-task-trigger:hover {
  background: var(--form-input-bg, #f0f4ff);
  border-color: var(--btn-primary, #667eea);
}

.add-task-plus {
  font-size: 1.1rem;
  line-height: 1;
}

.add-item-form {
  margin-top: 0.25rem;
  background: var(--form-input-bg, #f8fafc);
  border: 1px solid var(--form-input-border, #e6e6e6);
  border-radius: 10px;
  padding: 0.8rem;
}

.add-form-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.65rem;
}

.add-form-header h4 {
  color: var(--form-label, #444);
  font-size: 1rem;
  margin: 0;
}

.close-form-btn {
  background: none;
  border: none;
  color: var(--form-label, #888);
  font-size: 1rem;
  cursor: pointer;
  line-height: 1;
  padding: 0.1rem 0.3rem;
  border-radius: 4px;
}

.close-form-btn:hover {
  background: rgba(0, 0, 0, 0.06);
}

.toggle-image-btn {
  background: none;
  border: none;
  color: var(--btn-primary, #667eea);
  font-size: 0.88rem;
  font-weight: 600;
  cursor: pointer;
  padding: 0;
  margin-bottom: 0.8rem;
  display: block;
}

.toggle-image-btn:hover {
  text-decoration: underline;
}

.group {
  margin-bottom: 0.8rem;
}

.group input[type='text'] {
  width: 100%;
  padding: 0.6rem;
  border-radius: 7px;
  border: 1px solid var(--form-input-border, #e6e6e6);
  font-size: 1rem;
  background: var(--form-input-bg, #fff);
  box-sizing: border-box;
}

.item-form-actions {
  display: flex;
  gap: 0.5rem;
}

.small-btn {
  padding: 0.4rem 0.8rem;
  font-size: 0.85rem;
  min-width: 0;
}

@media (max-width: 600px) {
  .view {
    padding: 1.5rem 1.2rem;
  }

  .item-row {
    align-items: flex-start;
    flex-direction: column;
    gap: 0.6rem;
  }

  .item-actions {
    width: 100%;
    justify-content: flex-end;
  }
}
</style>
