<script setup lang="ts">
import { onMounted, ref, computed } from 'vue'
import { Calendar, Clock, RefreshCw, Loader2, AlertCircle, CheckCircle2, Timer } from '@lucide/vue'
import { apiFetch } from '../services/api'

interface CalendarJob {
  id: string
  name: string
  schedule: string
  lastRun: string | null
  nextRun: string | null
  status: string
}

interface UpcomingJob {
  id: string
  name: string
  nextRun: string
  schedule: string
}

const jobs = ref<CalendarJob[]>([])
const upcoming = ref<UpcomingJob[]>([])
const loading = ref(false)
const upcomingLoading = ref(false)
const error = ref('')
const upcomingError = ref('')

function formatSchedule(cron: string): string {
  const map: Record<string, string> = {
    '*/5 * * * *': 'Every 5 minutes',
    '*/10 * * * *': 'Every 10 minutes',
    '*/15 * * * *': 'Every 15 minutes',
    '*/30 * * * *': 'Every 30 minutes',
    '0 * * * *': 'Every hour',
    '0 */6 * * *': 'Every 6 hours',
    '0 */12 * * *': 'Every 12 hours',
    '0 0 * * *': 'Daily at midnight',
    '0 3 * * *': 'Daily at 03:00',
    '0 4 * * *': 'Daily at 04:00',
    '0 2 * * *': 'Daily at 02:00',
  }
  return map[cron] ?? cron
}

function formatDate(dateStr: string): string {
  const d = new Date(dateStr)
  const now = new Date()
  const diffMs = d.getTime() - now.getTime()
  const diffMin = Math.round(diffMs / 60000)

  const dateFormatted = d.toLocaleDateString('de-DE', {
    year: 'numeric', month: 'short', day: 'numeric',
    hour: '2-digit', minute: '2-digit',
  })

  if (diffMin < 0) {
    return `${dateFormatted} (${Math.abs(diffMin)}m ago)`
  }
  if (diffMin < 60) {
    return `${dateFormatted} (in ${diffMin}m)`
  }
  const diffHr = Math.round(diffMin / 60)
  if (diffHr < 24) {
    return `${dateFormatted} (in ${diffHr}h)`
  }
  return dateFormatted
}

function statusIcon(status: string) {
  if (status === 'completed') return CheckCircle2
  if (status === 'running') return Timer
  if (status === 'failed') return AlertCircle
  return Clock
}

function statusClass(status: string): string {
  if (status === 'completed') return 'status-completed'
  if (status === 'running') return 'status-running'
  if (status === 'failed') return 'status-failed'
  return 'status-idle'
}

async function loadJobs() {
  loading.value = true
  error.value = ''
  try {
    const response = await apiFetch('/api/v1/calendar')
    if (!response.ok) throw new Error('Failed to load jobs')
    jobs.value = await response.json()
  } catch (e) {
    error.value = e instanceof Error ? e.message : 'Failed to load calendar'
  } finally {
    loading.value = false
  }
}

async function loadUpcoming() {
  upcomingLoading.value = true
  upcomingError.value = ''
  try {
    const response = await apiFetch('/api/v1/calendar/upcoming')
    if (!response.ok) throw new Error('Failed to load upcoming jobs')
    upcoming.value = await response.json()
  } catch (e) {
    upcomingError.value = e instanceof Error ? e.message : 'Failed to load upcoming'
  } finally {
    upcomingLoading.value = false
  }
}

const sortedUpcoming = computed(() => {
  return [...upcoming.value].sort((a, b) => {
    return new Date(a.nextRun).getTime() - new Date(b.nextRun).getTime()
  })
})

onMounted(() => {
  loadJobs()
  loadUpcoming()
})
</script>

<template>
  <div class="page-heading">
    <div>
      <span class="eyebrow">SCHEDULER</span>
      <h1>Calendar</h1>
      <p>Scheduled jobs and cron tasks across the OpenClaw gateway.</p>
    </div>
    <button class="refresh" @click="loadJobs(); loadUpcoming()">
      <RefreshCw :size="15" :class="{ spin: loading || upcomingLoading }" />
      Refresh
    </button>
  </div>

  <div class="calendar-layout">
    <!-- Upcoming section -->
    <section class="calendar-panel upcoming-panel">
      <header class="calendar-panel-head">
        <Calendar :size="16" />
        <h2>Upcoming Executions</h2>
      </header>
      <template v-if="upcomingLoading">
        <div class="calendar-status">
          <Loader2 :size="16" class="spin" />
          Loading upcoming jobs...
        </div>
      </template>
      <template v-else-if="upcomingError">
        <div class="calendar-status error">{{ upcomingError }}</div>
      </template>
      <template v-else-if="sortedUpcoming.length">
        <div
          v-for="job in sortedUpcoming.slice(0, 5)"
          :key="job.id"
          class="upcoming-item"
        >
          <div class="upcoming-item-icon">
            <Clock :size="14" />
          </div>
          <div class="upcoming-item-info">
            <strong>{{ job.name }}</strong>
            <span class="upcoming-item-schedule">{{ formatSchedule(job.schedule) }}</span>
            <span class="upcoming-item-next">{{ formatDate(job.nextRun) }}</span>
          </div>
        </div>
      </template>
      <div v-else class="calendar-status">No upcoming jobs</div>
    </section>

    <!-- All jobs list -->
    <section class="calendar-panel all-jobs-panel">
      <header class="calendar-panel-head">
        <Timer :size="16" />
        <h2>All Scheduled Jobs</h2>
      </header>
      <template v-if="loading">
        <div class="calendar-status">
          <Loader2 :size="16" class="spin" />
          Loading jobs...
        </div>
      </template>
      <template v-else-if="error">
        <div class="calendar-status error">{{ error }}</div>
      </template>
      <template v-else-if="jobs.length">
        <div
          v-for="job in jobs"
          :key="job.id"
          class="job-item"
        >
          <div class="job-item-icon" :class="statusClass(job.status)">
            <component :is="statusIcon(job.status)" :size="14" />
          </div>
          <div class="job-item-info">
            <strong>{{ job.name }}</strong>
            <span class="job-item-id">{{ job.id }}</span>
            <span class="job-item-schedule">{{ formatSchedule(job.schedule) }}</span>
          </div>
          <div class="job-item-meta">
            <span :class="['job-status-badge', statusClass(job.status)]">{{ job.status }}</span>
            <small v-if="job.lastRun">Last: {{ formatDate(job.lastRun) }}</small>
            <small v-if="job.nextRun">Next: {{ formatDate(job.nextRun) }}</small>
          </div>
        </div>
      </template>
      <div v-else class="calendar-status">No scheduled jobs</div>
    </section>
  </div>
</template>

<style scoped>
.calendar-layout {
  display: grid;
  grid-template-columns: 1fr 2fr;
  gap: 12px;
  min-height: 360px;
}
.calendar-panel {
  border: 1px solid var(--line);
  border-radius: 9px;
  background: var(--panel);
  padding: 16px;
}
.calendar-panel-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-bottom: 12px;
  margin-bottom: 12px;
  border-bottom: 1px solid var(--line);
}
.calendar-panel-head h2 {
  margin: 0;
  font-size: 13px;
  font-weight: 600;
  color: #e8eaf0;
}
.calendar-status {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 32px;
  color: #7e8799;
  font-size: 11px;
}
.calendar-status.error {
  color: #e16e75;
}

/* Upcoming jobs */
.upcoming-item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px;
  border-radius: 7px;
}
.upcoming-item + .upcoming-item {
  border-top: 1px solid var(--line);
}
.upcoming-item-icon {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  border-radius: 6px;
  color: #a99cf5;
  background: rgba(139,124,246,.1);
}
.upcoming-item-info strong {
  display: block;
  font-size: 11px;
  margin-bottom: 2px;
  color: #e8eaf0;
}
.upcoming-item-schedule {
  display: block;
  font-size: 9px;
  color: #7e8799;
  margin-bottom: 2px;
}
.upcoming-item-next {
  display: block;
  font-size: 9px;
  color: #a99cf5;
  font-weight: 600;
}

/* All jobs */
.job-item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px;
  border-radius: 7px;
}
.job-item + .job-item {
  border-top: 1px solid var(--line);
}
.job-item-icon {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  border-radius: 6px;
}
.job-item-icon.status-completed {
  color: #27ae60;
  background: rgba(39, 174, 96, 0.12);
}
.job-item-icon.status-running {
  color: #3498db;
  background: rgba(52, 152, 219, 0.12);
}
.job-item-icon.status-failed {
  color: #e74c3c;
  background: rgba(231, 76, 60, 0.12);
}
.job-item-icon.status-idle {
  color: #7e8799;
  background: rgba(126, 135, 153, 0.12);
}
.job-item-info {
  flex: 1;
}
.job-item-info strong {
  display: block;
  font-size: 11px;
  margin-bottom: 2px;
  color: #e8eaf0;
}
.job-item-id {
  display: block;
  font-size: 9px;
  color: #6b7385;
  margin-bottom: 2px;
  font-family: monospace;
}
.job-item-schedule {
  display: block;
  font-size: 9px;
  color: #7e8799;
}
.job-item-meta {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 3px;
  flex-shrink: 0;
}
.job-item-meta small {
  font-size: 8px;
  color: #6b7385;
  text-align: right;
}
.job-status-badge {
  font-size: 8px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 2px 6px;
  border-radius: 4px;
}
.job-status-badge.status-completed {
  background: rgba(39, 174, 96, 0.15);
  color: #27ae60;
}
.job-status-badge.status-running {
  background: rgba(52, 152, 219, 0.15);
  color: #3498db;
}
.job-status-badge.status-failed {
  background: rgba(231, 76, 60, 0.15);
  color: #e74c3c;
}
.job-status-badge.status-idle {
  background: rgba(126, 135, 153, 0.15);
  color: #7e8799;
}
.spin {
  animation: spin 1s linear infinite;
}
@keyframes spin {
  to { transform: rotate(360deg); }
}

@media (max-width: 900px) {
  .calendar-layout {
    grid-template-columns: 1fr;
  }
}
</style>
