<script setup>
import {computed, onMounted, ref, watch} from 'vue'
import {useAuthStore} from '@/stores/Auth'
import {authService} from '@/api/authService'
import jsPDF from 'jspdf'
import autoTable from 'jspdf-autotable'
import dejavuFont from '@/fonts/DejaVuSans-normal.js'

const authStore = useAuthStore()

const isFetching = ref(false);
// данные из API
const plans = ref([])
const requests = ref([])
const districts = ref([])
const isLoaded = ref(false)
const groupManagers = ref([])
const subgroupManagers = ref([])
const selGroupManager = ref(null)
const selSubgroupManager = ref(null)

const page = ref(1);
const perPage = 50;
const totalPages = ref(1);

const summary = ref({
  planned: 0,
  completed: 0,
  remarks: 0,
  overdue: 0,
  remaining: 0,
});

// фильтры
const selDistrict = ref(null)
const selRequest = ref(null)
const selMonth = ref(null)

// для модалки
const showModal = ref(false)
const current = ref(null)
const hours = ref([])
const rows = ref([])
const direction = ref(1)
const fileInputs = ref({before: [], after: [], doc: []})
const previews = ref({before: [], after: [], doc: []})
const weather = ref('Ясно')
const weatherOptions = ['Ясно', 'Пасмурно', 'Дождь', 'Снег']
const lanesCount = ref(2)
const isUploading = ref(false)
const isAutoFill = ref(false); // true, если нажали 15/30/1/2 или «Заполнить из БД»
const uploadErrors = ref([])
const error = ref(null)

// Генерация случайного целого в диапазоне [min, max]
function randInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min
}

// функция расчёта:
function recalcByBase(baseMinutes) {
  rows.value.forEach(r => {
    const morningIdx = hours.value.indexOf('09:00')
    const afternoonIdx = hours.value.indexOf('13:00')
    const baseIdx = (r.counts[morningIdx] > 0 ? morningIdx : afternoonIdx)
    const origBaseValue = r.counts[baseIdx] || 0

    // переводим в "за час"
    const factor = 60 / baseMinutes
    const hourlyValue = Math.round(origBaseValue * factor)
    r.counts[baseIdx] = hourlyValue

    // пик — умножаем на 1.6
    const peakIdx = baseIdx === morningIdx
        ? hours.value.indexOf('08:00')
        : hours.value.indexOf('16:00')
    const peakValue = Math.round(hourlyValue * 1.6)
    r.counts[peakIdx] = peakValue

    // остальные — hourly ± rand [-3, +3], но не больше peak и не меньше 0
    r.counts.forEach((_, i) => {
      if (i === baseIdx || i === peakIdx) return
      const delta = randInt(-3, 3)
      const v = hourlyValue + delta
      r.counts[i] = Math.max(0, Math.min(peakValue, v))
    })
  })
}

const onCalc15 = () => {
  isAutoFill.value = true
  recalcByBase(15)
}
const onCalc30 = () => {
  isAutoFill.value = true
  recalcByBase(30)
}
const onCalc1h = () => {
  isAutoFill.value = true
  recalcByBase(60)
}
const onCalc2h = () => {
  isAutoFill.value = true
  recalcByBase(120)
}

const hourRanges = computed(() => {
  return hours.value.map(h => {
    const [hh, mm] = h.split(':').map(Number)
    const nextH = (hh + 1).toString().padStart(2, '0')
    // получим "08:00-09:00", "09:00-10:00"…
    return `${h}-${nextH}:${String(mm).padStart(2, '0')}`
  })
})

// рассчитываем месяц->планы
const monthsMap = computed(() => {
  const m = new Map()
  plans.value.forEach(p => {
    if (selDistrict.value && p.point.district.id !== selDistrict.value) return
    if (selRequest.value && p.request_id !== selRequest.value) return
    const mo = new Date(p.day).getMonth() + 1
    if (!m.has(mo)) m.set(mo, [])
    m.get(mo).push(p)
  })
  return m
})
const months = computed(() =>
    Array.from(monthsMap.value.keys()).sort().map(mo => ({
      value: mo,
      label: new Date(0, mo - 1).toLocaleString('ru', {month: 'long'})
    }))
)
const filtered = computed(() => {
  let arr = plans.value

  if (selDistrict.value) {
    arr = arr.filter(p => p.point.district.id === selDistrict.value)
  }

  if (selGroupManager.value) {
    arr = arr.filter(p => p.point.district.group_manager_id === selGroupManager.value)
  }

  if (selSubgroupManager.value) {
    arr = arr.filter(p => p.subgroup_manager_id === selSubgroupManager.value)
  }

  if (selRequest.value) {
    arr = arr.filter(p => p.request_id === selRequest.value)
  }

  if (selMonth.value) {
    arr = arr.filter(p => (new Date(p.day).getMonth() + 1) === selMonth.value)
  }

  return arr.map(plan => ({
    ...plan,
    isWorkedOut: isPlanWorkedOut(plan),
  }));
})

// формат даты
function formatDate(iso) {
  const d = new Date(iso)
  return d.toLocaleDateString('ru')
}

// часы
function makeHours(start, end) {
  const [h1, m1] = start.split(':').map(Number)
  const [h2] = end.split(':').map(Number)
  const out = []
  for (let h = h1; h < h2; h++) {
    // сохраняем тот же «mm» из start, но обычно это 00
    out.push(`${String(h).padStart(2, '0')}:${String(m1).padStart(2, '0')}`)
  }
  return out
}

function rowClass(plan) {
  // 1) если есть comment — жёлтый
  if (plan.comment?.trim()) return 'table-warning';
  // 2) если выполнено — зелёный
  if (plan.isWorkedOut) return 'table-success';
  // 3) если просрочено — красный
  if (new Date(plan.day) < new Date()) return 'table-danger';
  // 4) иначе — никакого
  return '';
}

// открываем форму
async function openForm(plan) {
  current.value = plan
  isUploading.value = false
  isAutoFill.value = false
  uploadErrors.value = []
  error.value = null

  // Сбрасываем все предыдущие зацепки по файлам
  fileInputs.value = {before: [], after: [], doc: []}
  previews.value = {before: [], after: [], doc: []}

  hours.value = makeHours(plan.timeStart, plan.timeEnd)
  direction.value = 1

  // 1) забираем категории
  const catRes = await authService.getVehicleCategories()
  const cats = catRes.data

  // 2) забираем существующие stats
  const statsRes = await authService.getPlanStats(plan.id)
  const raw = statsRes.data.records
  direction.value = statsRes.data.direction
  weather.value = statsRes.data.weather
  lanesCount.value = typeof statsRes.data.lanes_count === 'number'
      ? statsRes.data.lanes_count
      : 2;

  // 3) формируем строки
  rows.value = cats.map(cat => {
    const counts = hours.value.map(h =>
        raw[cat.key]?.[h] || 0
    )
    return {
      category_key: cat.key,
      label: cat.label,
      image_url: cat.image_url,
      counts
    }
  })


  // 4) файлы
  const filesRes = await authService.listPlanFiles(plan.id);
  const files = filesRes.data.files;
  ['before', 'after', 'doc'].forEach(type => {
    previews.value[type] = files
        .filter(f => f.type === type)
        .map(f => ({
          id: f.id,
          // оригинальная картинка
          url: f.url,
          // превьюшная (если есть), иначе та же оригинальная
          thumbUrl: f.thumbnailUrl || f.url,
          // имя используем для подписи
          name: (f.thumbnailUrl || f.url).split('/').pop()
        }));
  })

  showModal.value = true
  current.value.isWorkedOut = checkIsWorkedOut();

}

function isPlanWorkedOut(plan) {
  // план считается выполненным, только если есть хотя бы одна фото ДО и ПОСЛЕ (с сервера), и есть статистика
  const hasStats = plan.measurements_count > 0; // или план.stats && plan.stats.length > 0
  return hasStats && plan.before_count > 0 && plan.after_count > 0;
}

// выбор файлов
function onFile(type, e) {
  const files = Array.from(e.target.files)
  const items = files.map(f => ({
    file: f,
    url: URL.createObjectURL(f),
    status: 'pending',   // pending | success | error
    error: null
  }))
  previews.value[type] = items
  fileInputs.value[type] = items
  // сразу загружаем
  uploadBatch(type, items)
}

async function uploadBatch(type, items) {
  isUploading.value = true
  uploadErrors.value = []
  for (const item of items) {
    const fd = new FormData()
    fd.append('point_plan_id', current.value.id)
    fd.append('type', type)
    fd.append('file', item.file)
    try {
      await authService.uploadPlanFile(fd)
      item.status = 'success'
    } catch (e) {
      item.status = 'error'
      item.error = e.response?.data?.message || e.message
      uploadErrors.value.push(`Ошибка загрузки ${item.file.name}: ${item.error}`)
    }
  }
  isUploading.value = false
  current.value.isWorkedOut = checkIsWorkedOut();
}

// удалить превью
async function removePreview(type, item) {
  // 1) если этот превьюшный объект пришёл с сервера (есть id) — удаляем его там
  if (item.id) {
    try {
      await authService.deletePlanFile(item.id);
    } catch (e) {
      // Добавляем ошибку в uploadErrors, чтобы она показалась на экране
      const msg =
          e.response?.data?.message ||
          e.message ||
          'Не удалось удалить файл (ошибка сервера)';
      uploadErrors.value.push(`Ошибка удаления "${item.name || item.file?.name}": ${msg}`);
      return; // Не продолжаем удаление локально!
    }
  }
  // 2) чистим превью и, если файл не загружен, — входящий массив fileInputs
  previews.value[type] = previews.value[type].filter(x => x !== item);
  current.value.isWorkedOut = checkIsWorkedOut();
  // для локально добавленных файлов (до первой отправки) — убираем из fileInputs
  fileInputs.value[type] = fileInputs.value[type].filter(f => {
    // если item.url === URL.createObjectURL(f) — это локальный
    return URL.createObjectURL(f) !== item.url;
  });
}

function checkIsWorkedOut() {
  // Проверяем, что есть хотя бы один сохранённый замер по каждой категории (или допускаем нули)
  const hasStats = rows.value.length > 0; // если твои rows строятся из сохранённых данных, то это ок

  // Проверяем наличие хотя бы одной фото "до" и "после" — СЕРВЕРНЫЕ (у которых есть id)
  const hasBefore = previews.value.before.some(f => f.id);
  const hasAfter = previews.value.after.some(f => f.id);

  return hasStats && hasBefore && hasAfter;
}

let inflight = null;
async function reloadPlans() {
  if (inflight) return inflight; // коалесим параллельные вызовы
  isFetching.value = true;
  inflight = authStore.getPointsOperator({
    page: page.value,
    per_page: perPage,
    request_id: selRequest.value,
    district_id: selDistrict.value,
    subgroup_manager_id: selSubgroupManager.value,
    month: selMonth.value,
  }).then(({response, error}) => {
    if (response) {
      plans.value   = response.data.point_plans;
      totalPages.value = response.data.meta.last_page;
      summary.value    = response.data.summary;
    } else {
      console.error(error);
    }
  }).finally(() => {
    isFetching.value = false;
    inflight = null;
  });
  return inflight;
}

watch([selRequest, selDistrict, selSubgroupManager, selMonth], () => {
  page.value = 1;
  reloadPlans();
});

async function fillFromDB() {
  if (!current.value) return;
  isUploading.value = true;
  isAutoFill.value = true;
  error.value = null;

  try {
    // 1) Забираем оригинальные часы из плана (например ['13:00','14:00','15:00','16:00'])
    const planHours = [...hours.value];

    // 2) Курсор на бэкенд
    const thisDate = new Date(current.value.day);
    const prevYear = thisDate.getFullYear() - 1;
    const thisMonth = thisDate.getMonth() + 1;
    const {data: prevStats} = await authService.getPreviousStats(
        current.value.point_id,
        prevYear,
        thisMonth
    );

    if (!prevStats?.length) {
      error.value = 'Нет данных для автозаполнения';
      return;
    }

    // 3) Собираем map категорий → их часы
    const statsMap = prevStats.reduce((m, rec) => {
      m[rec.category_key] = rec.hours;
      return m;
    }, {});

    // 4) Принимаем порядок часов из prevStats[0].hours (Object.keys сохраняет порядок)
    const prevOrder = Object.keys(prevStats[0].hours);

    // 5) Пересобираем rows, мапя по индексу prevOrder → planHours
    rows.value = rows.value.map(row => {
      const baseMap = statsMap[row.category_key] || {};
      // базовый массив значений прошлого года в порядке prevOrder
      const baseArr = prevOrder.map(h => baseMap[h] || 0);
      const total = baseArr.reduce((s, v) => s + v, 0);

      // если суммарно было 0 — оставляем все нули
      if (total === 0) {
        return {
          ...row,
          counts: planHours.map(() => 0)
        };
      }

      // иначе для каждого часа (сохраняя длину) делаем шум ±2
      return {
        ...row,
        counts: baseArr.map(v => Math.max(0, v + randInt(-2, 2)))
      };
    });

    // hours.value остаётся прежним — planHours
    current.value.filledFromDB = true;
  } catch (e) {
    error.value = e.response?.data?.message || e.message || 'Ошибка автозаполнения';
  } finally {
    isUploading.value = false;
  }
}

// сохранить всё
async function save(closeAfter = true) {
  if (isUploading.value) return;
  current.value.isWorkedOut = checkIsWorkedOut();
  // 1) сохраняем статистику
  const records = []
  rows.value.forEach(r => {
    hours.value.forEach((h, i) => {
      records.push({
        category_key: r.category_key,
        hour: h,
        count: r.counts[i]
      })
    })
  })
  // передаём direction единым полем
  const payload = {
    records: records,           // ваш массив
    direction: direction.value,
    weather: weather.value,
    lanes_count: lanesCount.value,
    is_manual: !isAutoFill.value,
  }

  try {
    await authService.savePlanStats(current.value.id, payload)
  } catch (e) {
    // Сохраняем ошибку в error (возможны разные структуры ответа)
    error.value = e.response?.data?.message || e.message || 'Ошибка сохранения данных';
    return; // Не продолжаем дальше, если ошибка
  }

  if (!uploadErrors.value.length && closeAfter) {
    showModal.value = false
    await reloadPlans();
  }


  // 3) перезагрузили превью с сервера
  const filesRes = await authService.listPlanFiles(current.value.id)
  const files = filesRes.data.files;
  ['before', 'after', 'doc'].forEach(type => {
    previews.value[type] = files
        .filter(f => f.type === type)
        .map(f => ({
          id: f.id,
          // оригинальный файл
          url: f.url,
          // превью-шка (если есть), иначе сам оригинал
          thumbUrl: f.thumbnailUrl || f.url,
          name: (f.thumbnailUrl || f.url).split('/').pop(),
        }))
  })


  const hasBefore = previews.value.before.length > 0
  const hasAfter = previews.value.after.length > 0
  const done = hasBefore && hasAfter

  current.value.isWorkedOut = done

// 2) найдём тот же объект в plans.value и обновим там
  const idx = plans.value.findIndex(p => p.id === current.value.id)
  if (idx !== -1) {
    plans.value[idx].isWorkedOut = done
  }
}

function pickLatestRequest(list = []) {
  if (!list.length) return null
  // если есть created_at — сортируем по нему
  const withDate = list.filter(r => r.created_at)
  if (withDate.length) {
    return [...withDate].sort((a,b) => new Date(b.created_at) - new Date(a.created_at))[0]
  }
  // иначе — по id убыв
  return [...list].sort((a,b) => b.id - a.id)[0]
}

onMounted(async () => {
  isLoaded.value = false
  isFetching.value = true

  // ① Сначала заявки → выбрать последнюю
  const {response: rqRes, error: rqErr} = await authStore.getRequests()
  if (rqRes) {
    requests.value = rqRes.data
    const last = pickLatestRequest(requests.value)
    selRequest.value = last?.id ?? null   // это триггернет watch и подгрузит страницу 1
  } else {
    console.error(rqErr)
  }

  // ③ Справочники — параллельно, в фоне
  const [{response: dRes}, {response: gmRes}, {response: smRes}] = await Promise.all([
    authStore.getRefDistricts(),
    authStore.getRefManagers('group'),
    authStore.getRefManagers('subgroup'),
  ])
  if (dRes)  districts.value       = dRes.data
  if (gmRes) groupManagers.value   = gmRes.data
  if (smRes) subgroupManagers.value= smRes.data

  isLoaded.value = true
  isFetching.value = false
})

function generateIntensityPdf() {
  // ‣ create document
  const doc = new jsPDF({orientation: 'portrait', unit: 'mm', format: 'a4'})
  // ‣ embed font so Cyrillic works
  doc.addFileToVFS('DejaVuSans.ttf', dejavuFont)
  doc.addFont('DejaVuSans.ttf', 'DejaVuSans', 'normal')
  doc.setFont('DejaVuSans')
  doc.setFontSize(10)

  const margin = 15
  let y = margin

  // — HEADER FIELDS —
  doc.text(`ФОРМА УЧЕТА ИНТЕНСИВНОСТИ ДВИЖЕНИЯ`, margin, y);
  y += 6
  doc.text(`Автомобильная дорога: ${current.value.point.record_number} ${current.value.point.name}`, margin, y);
  y += 6
  doc.text(`Место учета: ${current.value.point.accounting_point} км`, margin, y);
  y += 6
  const segFrom = Number(current.value.point.segment_from) || 0;
  const segTo = (
      (current.value.point.segment_to !== undefined && current.value.point.segment_to !== null)
          ? Number(current.value.point.segment_to)
          : (current.value.point.length !== undefined && current.value.point.length !== null
              ? Number(current.value.point.length)
              : 0)
  );

  const segmentLine = `Участок (перегон): ${segFrom.toFixed(3)} км — ${segTo.toFixed(3)} км`;

  doc.text(segmentLine, margin, y);
  y += 6
  doc.text(`Число полос движения: ${lanesCount.value ?? '____'}`, margin, y);
  y += 6
  function getDayOfWeekRus(dateString) {
    const date = new Date(dateString)
    const days = [
      'воскресенье',
      'понедельник',
      'вторник',
      'среда',
      'четверг',
      'пятница',
      'суббота'
    ]
    return days[date.getDay()]
  }

// Форматированная дата + день недели
  function getFormattedDateWithWeekday(dateString) {
    const d = new Date(dateString)
    const datePart = d.toLocaleDateString('ru-RU')
    const weekday = getDayOfWeekRus(dateString)
    return `${datePart}, ${weekday}`
  }

// ... в функции generateIntensityPdf:
  doc.text(
      `Дата проведения учета: ${getFormattedDateWithWeekday(current.value.day)}`,
      margin, y
  )
  y += 6
  doc.text(
      `Время учета: начало ${current.value.timeStart}  окончание ${current.value.timeEnd}`,
      margin, y
  );
  y += 6
  const directionLabel = direction.value ? 'Попутное' : 'Встречное';
  doc.text(
      `Направление движения: ${directionLabel}`,
      margin, y
  );
  y += 10
  y += 10

  // — DATA TABLE —
  doc.setFontSize(9)

  console.log('HOURS:', hours.value)
  console.log('rows:', rows.value)

  const tableColumnDefs = [
    {header: 'Категория', dataKey: 'label'},
    ...hours.value.map((h, i) => ({
      header: h
          ? `${h}–${(parseInt(h.slice(0, 2)) + 1).toString().padStart(2, '0')}:${h.slice(3)}`
          : '',
      dataKey: 'h' + i
    })),
    {header: 'Итого', dataKey: 'total'}
  ]
  const tableRows = rows.value.map(row => {
    const obj = {label: row.label}
    row.counts.forEach((cnt, i) => obj['h' + i] = cnt)
    obj.total = row.counts.reduce((s, n) => s + n, 0)
    return obj
  })

  autoTable(doc, {
    startY: y,
    margin: {left: margin, right: margin},
    head: [tableColumnDefs.map(c => c.header)],
    body: tableRows.map(r => tableColumnDefs.map(c => r[c.dataKey])),
    styles: {font: 'DejaVuSans', fontSize: 8, cellPadding: 2, lineColor: [0, 0, 0], lineWidth: 0.1},
    headStyles: {
      fillColor: [255,255,255], // белый фон
      textColor: [0,0,0],       // чёрный текст
      fontStyle: 'normal',
      lineColor: [0, 0, 0],     // чёрные границы
      lineWidth: 0.1,
    },
    theme: 'grid',
  })

  // — SIGNATURES —
  const finalY  = doc.lastAutoTable.finalY + 15;
  const col1    = margin;
  function getManagerFioById(id) {
    const manager = subgroupManagers.value.find(m => m.id === id)
    if (!manager) return ''
    // Если есть fullName — используем его, иначе собираем ФИО из частей:
    return manager.fullName ||
        [manager.last_name, manager.first_name, manager.middle_name].filter(Boolean).join(' ')
  }
  const fio = getManagerFioById(current.value.subgroup_manager_id);
  const sigLines = [
    { title: 'Учет интенсивности выполнил', name: fio },
    { title: 'Подпись',                     name: '___________________________' },
    { title: 'Дата',                        name: '____ ______ 20__ г.' }
  ];
  sigLines.forEach((line, idx) => {
    const y0 = finalY + idx * 7;
    doc.text(line.title + ':', col1, y0);
    doc.text(line.name, col1 + 60, y0);
  });

  doc.save(`Форма_учета_интенсивности_${current.value.point.record_number}.pdf`)
}

async function saveAndPrintPdf() {
  await save(false); // ждем пока все сохранится
  // если не было ошибки
  if (!error.value) {
    generateIntensityPdf();
  } else {
    // можно показать уведомление, если хочешь
    alert('Ошибка при сохранении! Документ не будет напечатан. ' + error.value);
  }
}

</script>

<template>
  <div class="container py-4">
    <div v-if="!isLoaded" class="text-center py-5">
      <div class="spinner-border"></div>
      <p>Загрузка данных…</p>
    </div>

    <div v-else>
      <!-- === Блок сводки === -->
      <div class="row row-cols-2 row-cols-md-5 text-center mb-4">
        <div class="col py-2 border">
          <strong>Запланировано:</strong><br>{{ summary.planned }}
        </div>
        <div class="col py-2 border text-success">
          <strong>Выполнено:</strong><br>{{ summary.completed }}
        </div>
        <div class="col py-2 border text-warning">
          <strong>Замечания:</strong><br>{{ summary.remarks }}
        </div>
        <div class="col py-2 border text-danger">
          <strong>Просрочено:</strong><br>{{ summary.overdue }}
        </div>
        <div class="col py-2 border">
          <strong>Осталось:</strong><br>{{ summary.remaining }}
          <br><small>до {{ summary.deadline ? formatDate(summary.deadline) : '—' }}</small>
        </div>
      </div>
      <!-- Фильтры -->
      <div class="row row-cols-2 row-cols-md-5 g-2 mb-3">
        <div class="col">
          <select v-model="selDistrict" class="form-select">
            <option :value="null">— Район —</option>
            <option v-for="d in districts" :key="d.id" :value="d.id">{{ d.name }}</option>
          </select>
        </div>
        <div class="col">
          <select v-model="selGroupManager" class="form-select">
            <option :value="null">— Менеджер группы —</option>
            <option v-for="m in groupManagers" :key="m.id" :value="m.id">{{ m.fullName }}</option>
          </select>
        </div>
        <div class="col">
          <select v-model="selSubgroupManager" class="form-select">
            <option :value="null">— Менеджер подгруппы —</option>
            <option v-for="m in subgroupManagers" :key="m.id" :value="m.id">{{ m.fullName }}</option>
          </select>
        </div>
        <div class="col"><select v-model="selRequest" class="form-select">
          <option :value="null">— Заявка —</option>
          <option v-for="r in requests" :key="r.id" :value="r.id">{{ r.name }}</option>
        </select></div>
        <div class="col"><select v-model="selMonth" class="form-select">
          <option :value="null">— Месяц —</option>
          <option v-for="m in months" :key="m.value" :value="m.value">{{ m.label }}</option>
        </select></div>
      </div>

      <div v-if="isFetching" class="loader-overlay">
        <div class="spinner-border text-primary" role="status"><span class="visually-hidden">Загрузка...</span></div>
      </div>
      <div v-show="!isFetching">
        <template v-if="plans.length > 0">
        <!-- Таблица планов -->
        <table class="table  table-hover plan-table">
          <thead>
          <tr>
            <th>Дата / Время</th>
            <th>№</th>
            <th>Наименование ПУИДД</th>
            <th class="d-none d-md-table-cell text-center">Данные</th>
            <th class="d-none d-md-table-cell text-center">ДО</th>
            <th class="d-none d-md-table-cell text-center">ПОСЛЕ</th>
            <th class="d-none d-md-table-cell text-center">Док.</th>
          </tr>
          </thead>
          <tbody>
          <tr
              v-for="plan in filtered"
              :key="plan.id"
              @click="openForm(plan)"
              :class="rowClass(plan)"
          >
            <td>
              <div class="fw-bold">{{ formatDate(plan.day) }}</div>
              <div class="small text-muted">{{ plan.timeStart }}–{{ plan.timeEnd }}</div>
            </td>
            <td>{{ plan.point.record_number }}</td>
            <td>
              <!-- само имя -->
              <div class="fw-bold">{{ plan.point.name }}</div>
              <!-- иконки: только на XS (скрыты на md и крупнее) -->
              <div class="d-block d-md-none mt-1">
                <i v-if="plan.measurements_count" class="bi bi-check-circle-fill text-success me-1"></i>
                <i v-else class="bi bi-x-circle-fill text-danger me-1"></i>

                <i v-if="plan.before_count" class="bi bi-image-fill text-success me-1"></i>
                <i v-else class="bi bi-image text-muted me-1"></i>

                <i v-if="plan.after_count" class="bi bi-image-fill text-success me-1"></i>
                <i v-else class="bi bi-image text-muted me-1"></i>

                <i v-if="plan.doc_count" class="bi bi-file-earmark-fill text-success"></i>
                <i v-else class="bi bi-file-earmark text-muted"></i>
              </div>
            </td>
            <!-- на MD+ оставляем отдельные колонки, на XS они скрыты -->
            <td class="d-none d-md-table-cell text-center">
              <i v-if="plan.measurements_count" class="bi bi-check-circle-fill text-success"></i>
              <i v-else class="bi bi-x-circle-fill text-danger"></i>
            </td>
            <td class="d-none d-md-table-cell text-center">
              <i v-if="plan.before_count" class="bi bi-image-fill text-success"></i>
              <i v-else class="bi bi-image text-muted"></i>
            </td>
            <td class="d-none d-md-table-cell text-center">
              <i v-if="plan.after_count" class="bi bi-image-fill text-success"></i>
              <i v-else class="bi bi-image text-muted"></i>
            </td>
            <td class="d-none d-md-table-cell text-center">
              <i v-if="plan.doc_count" class="bi bi-file-earmark-fill text-success"></i>
              <i v-else class="bi bi-file-earmark text-muted"></i>
            </td>
          </tr>
          </tbody>
        </table>
        </template>
        <div v-else class="text-center py-5 text-muted">
          <i class="bi bi-inbox fs-1 mb-3"></i>
          <p class="fs-5">Нет данных для отображения</p>
        </div>
      </div>

      <div class="d-flex justify-content-between my-3">
        <button class="btn btn-sm btn-outline-primary"
                :disabled="page === 1"
                @click="page--; reloadPlans();">
          ← Назад
        </button>

        <span>Страница {{ page }} из {{ totalPages }}</span>

        <button class="btn btn-sm btn-outline-primary"
                :disabled="page >= totalPages"
                @click="page++; reloadPlans();">
          Вперед →
        </button>
      </div>

      <!-- Модалка -->
      <div v-if="showModal" class="modal-backdrop d-flex align-items-center justify-content-center">
        <div class="bg-white p-4 rounded position-relative"
             style="max-width:900px; max-height:90vh; overflow:auto">

          <button class="btn-close position-absolute top-0 end-0 m-3"
                  @click="showModal = false" aria-label="Закрыть"></button>

          <h4>ФОРМА УЧЕТА ИНТЕНСИВНОСТИ ДВИЖЕНИЯ</h4>
          <p>
            <b>а/д:</b> {{ current.point.record_number }} {{ current.point.name }}<br>
            <b>км участка:</b> {{ current.point.accounting_point }}<br>
            <b>дата:</b> {{ formatDate(current.day) }}<br>
            <b>время:</b> {{ current.timeStart }}–{{ current.timeEnd }}
          </p>
          <div class="mb-3">
            <label>Количество полос:</label>
            <select v-model.number="lanesCount"
                    class="form-select w-auto d-inline-block ms-2"
                    style="width: 100px;">
              <option :value="2">2</option>
              <option :value="3">3</option>
              <option :value="4">4</option>
            </select>
          </div>
          <div class="mb-3">
            <label>Направление:</label>
            <select v-model="direction" class="form-select w-auto d-inline-block ms-2">
              <option :value="1">Попутное</option>
              <option :value="0">Встречное</option>
            </select>
          </div>
          <div class="mb-3">
            <label>Погода:</label>
            <select v-model="weather" class="form-select d-inline-block w-auto ms-2">
              <option v-for="w in weatherOptions" :key="w" :value="w">{{ w }}</option>
            </select>
          </div>

          <table class="table table-bordered mb-3 modal-table">
            <thead>
            <tr>
              <th>Категория</th>
              <th
                  v-for="(range, i) in hourRanges"
                  :key="range"
                  class="text-center"
                  :class="{ 'table-warning': hours[i] === '09:00' || hours[i] === '13:00' }"
              >
                {{ range }}
              </th>
              <th class="text-center">Итого</th>
            </tr>
            </thead>
            <tbody>
            <tr v-for="row in rows" :key="row.category_key">
              <td class="category-cell d-flex align-items-center">
                <img :src="row.image_url"
                     style="width:40px; height:40px; object-fit:contain"
                     class="me-2"
                     alt="">
                <span class="category-label">{{ row.label }}</span>
              </td>
              <td
                  v-for="(h, i) in hours"
                  :key="h"
                  :class="{ 'table-warning': h === '09:00' || h === '13:00' }"
              >
                <input type="number" min="0" v-model.number="row.counts[i]" class="form-control form-control-sm">
              </td>
              <td class="text-center">{{ row.counts.reduce((s, n) => s + n, 0) }}</td>
            </tr>
            </tbody>
          </table>

          <div class="d-flex gap-2 mb-4">
            <button class="btn btn-outline-primary btn-sm" @click="onCalc15">15 мин.</button>
            <button class="btn btn-outline-primary btn-sm" @click="onCalc30">30 мин.</button>
            <button class="btn btn-outline-primary btn-sm" @click="onCalc1h">1 час</button>
            <button class="btn btn-outline-primary btn-sm" @click="onCalc2h">2 часа</button>
          </div>
          <div class="d-flex gap-2 mb-4">
            <button
                class="btn btn-warning btn-sm "
                @click="fillFromDB"
                style="font-weight: bold;"
            >
              <i class="bi bi-cloud-download me-1"></i>
              Заполнить из БД
            </button>

            <button class="btn btn-outline-primary btn-sm" @click="saveAndPrintPdf">
              Печать формы в PDF
            </button>
          </div>

          <!-- Загрузка и превью файлов -->
          <div class="row g-3 mb-4">
            <div class="col-md-4">
              <label class="form-label"><strong>Фото ДО:</strong></label>
              <input type="file" multiple class="form-control" @change="e => onFile('before', e)">
            </div>
            <div class="col-md-4">
              <label class="form-label"><strong>Фото ПОСЛЕ:</strong></label>
              <input type="file" multiple class="form-control" @change="e => onFile('after', e)">
            </div>
            <!--div class="col-md-4">
              <label class="form-label"><strong>Документы:</strong></label>
              <input type="file" multiple class="form-control" @change="e => onFile('doc', e)">
            </div -->
          </div>
          <!-- Показываем ошибки загрузки -->
          <div v-if="uploadErrors.length" class="alert alert-danger mt-3">
            <ul class="mb-0">
              <li v-for="err in uploadErrors" :key="err">{{ err }}</li>
            </ul>
          </div>
          <div v-if="error" class="alert alert-danger mb-3">
            {{ error }}
          </div>

          <div class="mb-4">
            <div v-for="(items, type) in previews" :key="type" class="mb-4">
              <h6 class="text-uppercase text-muted mb-2">
                {{ type === 'before' ? 'Фото ДО' : type === 'after' ? 'Фото ПОСЛЕ' : 'Документы' }}
              </h6>
              <div class="d-flex flex-wrap gap-3">
                <div
                    v-for="f in items"
                    :key="f.url"
                    class="preview-card position-relative rounded shadow-sm p-2"
                >
                  <div v-if="f.status === 'pending'"
                       class="spinner-border position-absolute top-50 start-50 translate-middle"></div>
                  <i v-if="f.status === 'error'"
                     class="bi bi-exclamation-circle-fill text-danger position-absolute top-0 end-0"></i>
                  <a v-if="/\.(jpe?g|png|gif|webp)$/i.test(f.thumbUrl)"
                     :href="f.url"
                     target="_blank"
                     class="d-block"
                  >
                    <img :src="f.thumbUrl" class="preview-img" alt="preview"/>
                  </a>
                  <video
                      v-else-if="/\.(mp4|webm|ogg)$/i.test(f.url)"
                      :src="f.url"
                      controls
                      class="preview-img"
                  ></video>
                  <a
                      v-else
                      :href="f.url"
                      target="_blank"
                      class="d-block small text-truncate mt-1"
                      style="max-width: 120px"
                  >
                    {{ f.file?.name || f.name }}
                  </a>
                  <button
                      class="btn btn-sm btn-close position-absolute top-0 end-0 m-1"
                      @click="removePreview(type, f)"
                  ></button>
                </div>
              </div>
            </div>
          </div>

          <!-- … кнопки Отмена/Сохранить … -->
          <div class="d-flex justify-content-between mt-4 mb-3">
            <button
                class="btn btn-secondary btn-lg"
                @click="showModal = false"
                :disabled="isUploading"
            >Отмена
            </button>
            <button
                class="btn btn-primary btn-lg"
                @click="save()"
                :disabled="isUploading || uploadErrors.length > 0"
            >
              <span v-if="isUploading" class="spinner-border spinner-border-sm me-2"></span>
              Сохранить
            </button>
          </div>


        </div>
      </div>
    </div>
  </div>
</template>

<style scoped>

.loader-overlay {
  position: fixed;
  inset: 0;
  background: rgba(255,255,255,0.6);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1050; /* над модалками */
}

.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, .5);
  z-index: 1000;
  /* flex-контейнер уже получил align-items и justify-content */
}

.preview-card {
  width: 120px;
  height: 120px;
  background-color: #f8f9fa;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  position: relative;
  border: 1px solid #dee2e6;
}

.preview-img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  border-radius: 0.25rem;
}

/* На телефонах (макс. ширина 576px) сужаем шрифт в <th> */
@media (max-width: 576px) {
  /* 1) Таблица вписывается в 100% контейнера */
  .plan-table {
    width: 100% !important;
    font-size: 0.75rem;
  }

  /* 2) Позволяем ячейкам переносить текст */
  .plan-table th,
  .plan-table td {
    white-space: normal !important;
    word-break: break-word;
    padding: 0.3rem 0.4rem;
  }

  /* 3) Сужаем шапку */
  .plan-table th:first-child,
  .plan-table td:first-child {
    width: 20%;
  }

  .modal-table {
    font-size: 0.75rem;
    width: 100% !important;
  }

  /* 2) Делаем перенос текстов */
  .modal-table th,
  .modal-table td {
    white-space: normal !important;
    word-break: break-word;
    padding: 0.3rem 0.4rem;
  }

  /* 3) В первой колонке переворачиваем flex из row в column */
  .modal-table .category-cell {
    flex-direction: column !important;
    align-items: center !important;
    text-align: center;
  }

  /* 4) Сам текст под картинкой чуть мельче и с отступом сверху */
  .modal-table .category-cell .category-label {
    font-size: 0.6rem;
    margin-top: 0.25rem;
  }

  /* Уменьшаем отступы и шрифт у инпутов и селектов в модалке */
  .modal-backdrop .form-control,
  .modal-backdrop .form-select {
    padding: 0.25rem 0.5rem !important;
    font-size: 0.75rem !important;
  }

  /* Сужаем заголовки и уменьшает отступы в заголовке модалки */
  .modal-backdrop h4 {
    font-size: 1rem;
    margin-bottom: 0.5rem !important;
  }

  .modal-backdrop p {
    font-size: 0.75rem;
    margin-bottom: 0.5rem !important;
  }

  /* Уменьшаем отступы вокруг кнопок */
  .modal-backdrop .btn {
    padding: 0.25rem 0.5rem;
    font-size: 0.75rem;
  }

  /* Сокращаем поля таблицы */
  .modal-backdrop .modal-table input {
    padding: 0.15rem 0.3rem;
    font-size: 0.7rem;
    height: auto;
  }

  /* Слегка уменьшаем общий padding модалки */
  .modal-backdrop > div {
    padding-bottom: calc(env(safe-area-inset-bottom) + 1rem) !important;
  }

  .modal-backdrop .btn-lg {
    padding: 0.75rem 1rem;
    font-size: 1rem;
  }
}

</style>
