<script setup lang="ts">
import {
  ref,
  reactive,
  onMounted,
  watch,
  onBeforeUnmount,
  computed,
  nextTick,
} from 'vue'
import { useQuasar } from 'quasar'
import { format } from 'date-fns'
import Camera from 'simple-vue-camera'
import { storeToRefs } from 'pinia'

import config from '@/app.config'
import http from '@/plugins/http'
import { useCounterMixin } from '@/stores/mixin'
import { usePermissions } from '@/composables/usePermissions'
import { usePrivacyStore } from '@/stores/privacy'
import { usePositionKeycloakStore } from '@/stores/positionKeycloak'
import { useSocketStore } from '@/stores/socket'

import type { FormRef, OptionReason } from '@/interface/response/checkin'

import MapCheck from '@/components/AscGISMap.vue'

const mixin = useCounterMixin()
const { date2Thai, showLoader, hideLoader, messageError, dialogConfirm } = mixin
const $q = useQuasar()
const {
  cameraPermissionState,
  checkPrivacyAccepted,
  syncPermissionStates,
  requestCameraPermission,
} = usePermissions()
const socketStore = useSocketStore()
const { notificationCounter } = storeToRefs(socketStore)
const privacyStore = usePrivacyStore()
const positionKeycloakStore = usePositionKeycloakStore()
const MOCK_CHECK_DELAY_MS = 800

// เช็คว่าผู้ใช้มีข้อมูลสังกัดหรือไม่
const hasOrganization = computed(() => {
  const data = positionKeycloakStore.dataPositionKeycloak
  if (!data || !data.organization) return false

  const org = data.organization
  // ตรวจสอบว่ามีค่าที่ไม่ใช่ null, undefined หรือ string ว่าง
  const hasValue = (val: string | null | undefined) => val && val.trim() !== ''

  return !!(
    hasValue(org.root) ||
    hasValue(org.child1) ||
    hasValue(org.child2) ||
    hasValue(org.child3) ||
    hasValue(org.child4)
  )
})

const modalTime = ref<boolean>(false) // Dailog ลงเวลาเข้างานของคุณ
const checkStatus = ref<string>('')
const statusCheckin = ref<boolean>(true) // สถานะเวลา เข้า,ออก

const checkDate = ref<boolean | null>(null)
const msgCheckTime = ref<string>('') // ข้อความแจ้งเตือน
const isDisabledCheckTime = ref<boolean>(true) // ข้อความแจ้งเตือน
const isErr = ref<boolean | null>(null) // ข้อความแจ้งเตือน
const endTimeAfternoon = ref<string>('12:00:00') //เวลาเช็คเอาท์ตามรอบ
const crossDayCheckoutWarning = ref<string>('')

const isLoadingCheckTime = ref<boolean>(false) // ตัวแปรสำหรับการโหลด
const disabledBtn = ref<boolean>(false)

function parseApiDateTime(value?: string | null) {
  if (!value) return null

  const normalizedValue = value.includes('T') ? value : value.replace(' ', 'T')
  const parsedDate = new Date(normalizedValue)
  return Number.isNaN(parsedDate.getTime()) ? null : parsedDate
}

function isSameCalendarDay(firstDate: Date, secondDate: Date) {
  return (
    firstDate.getFullYear() === secondDate.getFullYear() &&
    firstDate.getMonth() === secondDate.getMonth() &&
    firstDate.getDate() === secondDate.getDate()
  )
}

function updateCrossDayCheckoutWarning(checkInTime?: string | null) {
  crossDayCheckoutWarning.value = ''

  const checkInDate = parseApiDateTime(checkInTime)
  if (!checkInDate) return

  if (!isSameCalendarDay(checkInDate, new Date())) {
    crossDayCheckoutWarning.value = `คุณยังไม่ได้ลงเวลาออกของวันที่ ${date2Thai(
      checkInDate
    )} กรุณาลงเวลาออกก่อน จึงจะลงเวลาเข้าของวันนี้ได้`
  }
}

/**
 * fetch เช็คเวลาต้องลงเวลาเข้าหรือออกงาน
 */
async function fetchCheckTime(load: any = true) {
  if (load) isLoadingCheckTime.value = true
  await http
    .get(config.API.checkTime())
    .then(async (res) => {
      const data = await res.data.result

      statusCheckin.value = data.checkInId ? false : true
      checkInId.value = data.checkInId ? data.checkInId : ''
      updateCrossDayCheckoutWarning(data.checkInTime)
      endTimeAfternoon.value = data.endTimeAfternoon
      isDisabledCheckTime.value = isDisabledCheckTime.value ? true : false
    })
    .catch((err) => {
      if (err.response.status === 500) {
        isErr.value = true
        isDisabledCheckTime.value = true
        msgCheckTime.value = err.response.data.message
      } else messageError($q, err)
    })
    .finally(() => {
      if (load) isLoadingCheckTime.value = false
    })
}

/** ref อัพเดทเวลา*/
const dateNow = ref<Date>(new Date())
const Thai = ref<Date>(dateNow.value)
const formattedS = ref()
const formattedM = ref()
const formattedH = ref()
const formattedHH = ref()
const formattedA = ref()
const clockInterval = ref<ReturnType<typeof setInterval> | undefined>()

/** function อัพเดทเวลา*/
function updateClock() {
  const date = new Date()
  const hh = format(date, 'HH')
  const mm = format(date, 'mm')
  const ss = format(date, 'ss')
  const HH = format(date, 'hh')
  const A = format(date, 'a')

  formattedS.value = ss
  formattedM.value = mm
  formattedH.value = hh
  formattedHH.value = HH
  formattedA.value = A
}

/** form-data */
const formLocation = reactive({
  lat: 0, //พิกัดละติจูด
  lng: 0, //พิกัดลองจิจูด
  POI: '', //ชื่อสถานที่
})
const workplace = ref<string>('in-place') //ณ สถานที่ตั้ง, นอกสถานที่ตั้ง
const useLocation = ref<string>('') //กรณีเลือกนอกสถานที่ตั้ง ต้องระบุข้อมูลชื่อสถานะที่
const fileImg = ref<any>() //รูปถ่ายสถานที่
const remark = ref<string>('') //ข้อความหมายเหตุที่ต้องการระบุเพิ่ม
const checkInId = ref<string>('') //Id ลงเวลา check-in ล่าสุดที่ยังไม่ลงเวลาออก
const MIN_CAPTURE_FILE_SIZE_BYTES = 5 * 1024

/**
 *  funciton เรียกพิกัดละติจูด พิกัดลองติจูด
 * @param location  พิกัดละติจูด พิกัดลองติจูด
 * @param namePOI ชื่อสถานที่ ได้มาจากระบบ ArcGis ของกองสารสนเทศภูมิศาสตร์
 */
async function updateLocation(
  latitude: number,
  longitude: number,
  namePOI: string
) {
  formLocation.lat = latitude
  formLocation.lng = longitude
  formLocation.POI = namePOI
}

function resetLocationForRetry() {
  formLocation.lat = 0
  formLocation.lng = 0
  formLocation.POI = ''
}

function getCurrentPositionAsync(options?: PositionOptions) {
  return new Promise<GeolocationPosition>((resolve, reject) => {
    navigator.geolocation.getCurrentPosition(resolve, reject, options)
  })
}

function wait(ms: number) {
  return new Promise((resolve) => setTimeout(resolve, ms))
}

function getCameraVideoElement() {
  return camera.value?.video
}

function prepareInlineCameraVideo(videoElement: HTMLVideoElement) {
  videoElement.autoplay = true
  videoElement.muted = true
  videoElement.setAttribute('autoplay', '')
  videoElement.setAttribute('muted', '')
  videoElement.setAttribute('playsinline', '')
  videoElement.setAttribute('webkit-playsinline', 'true')
}

function isInlineCameraPreviewReady(videoElement: HTMLVideoElement) {
  return (
    Boolean(videoElement.srcObject) &&
    videoElement.videoWidth > 0 &&
    videoElement.videoHeight > 0 &&
    videoElement.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA
  )
}

async function waitForCameraInstance() {
  for (let attempt = 0; attempt < 10; attempt += 1) {
    await nextTick()

    if (camera.value) {
      return camera.value
    }

    await wait(50)
  }

  return null
}

async function stopInlineCamera() {
  camera.value?.stop()
  cameraIsOn.value = false
}

async function remountInlineCameraComponent() {
  await stopInlineCamera()
  cameraMountKey.value += 1
  await nextTick()
  await wait(INLINE_CAMERA_REMOUNT_DELAY_MS)
}

async function ensureInlineCameraPreviewReady() {
  const deadline = Date.now() + INLINE_CAMERA_PREVIEW_TIMEOUT_MS
  let lastPlaybackError: unknown = null

  while (Date.now() < deadline) {
    const videoElement = getCameraVideoElement()

    if (videoElement) {
      prepareInlineCameraVideo(videoElement)

      if (isInlineCameraPreviewReady(videoElement)) {
        return
      }

      if (videoElement.srcObject) {
        try {
          await videoElement.play()
        } catch (error) {
          lastPlaybackError = error
        }
      }
    }

    await wait(INLINE_CAMERA_PREVIEW_POLL_MS)
  }

  if (lastPlaybackError instanceof Error) {
    throw lastPlaybackError
  }

  throw new Error('Inline camera preview did not become ready in time')
}

async function syncActiveCameraDevices() {
  const devices: any = await camera.value?.devices(['videoinput'])
  if (!devices) {
    return
  }

  availableCameras.value = devices

  const activeId = camera.value?.currentDeviceID()
  const activeIdx = activeId
    ? devices.findIndex((device: any) => device.deviceId === activeId)
    : -1
  currentCameraIndex.value = activeIdx >= 0 ? activeIdx : 0
  currentCameraType.value = resolveCameraType(
    devices[currentCameraIndex.value]?.label || '',
    'front'
  )
}

async function startInlineCamera(recoveryAttempt = 0): Promise<void> {
  const cameraInstance = await waitForCameraInstance()

  if (!cameraInstance) {
    throw new Error('Camera component is unavailable')
  }

  try {
    currentCameraType.value = 'front'
    await cameraInstance.start()
    await ensureInlineCameraPreviewReady()
    await syncActiveCameraDevices()
    cameraIsOn.value = true
  } catch (error) {
    await stopInlineCamera()

    if (recoveryAttempt < MAX_INLINE_CAMERA_RECOVERY_ATTEMPTS) {
      await remountInlineCameraComponent()
      return startInlineCamera(recoveryAttempt + 1)
    }

    throw error
  }
}

async function getDelayedFreshPosition() {
  const firstPosition = await getCurrentPositionAsync({
    enableHighAccuracy: true,
    timeout: 10000,
    maximumAge: 0,
  })

  // Only add delay in development mode for testing
  if (import.meta.env.DEV) {
    await wait(MOCK_CHECK_DELAY_MS)
  }

  try {
    return await getCurrentPositionAsync({
      enableHighAccuracy: true,
      timeout: 10000,
      maximumAge: 0,
    })
  } catch (error) {
    return firstPosition
  }
}

const location = ref<string>('') // พื้นที่ใกล้เคียง
const model = ref<string>('') //  สถานที่ทำงาน
// ตัวเลือกสถานที่ทำงาน
const options = ref<OptionReason[]>([
  { value: 'ปฏิบัติงานที่บ้าน', text: 'ปฏิบัติงานที่บ้าน (WFH)' },
  { value: 'ปฏิบัติงานนอกสถานที่', text: 'ปฏิบัติงานนอกสถานที่ (WFA)' },
  // { value: 'ลืมลงเวลาเริ่มปฏิบัติราชการ', text: 'ลืมลงเวลาเริ่มปฏิบัติราชการ' },
  // { value: 'ลืมลงเวลาเลิกปฏิบัติราชการ', text: 'ลืมลงเวลาเลิกปฏิบัติราชการ' },
  {
    value: 'ไปประชุม / อบรม / สัมมนา',
    text: 'ไปประชุม / อบรม / สัมมนา',
  },
  // { value: 'ลางานครึ่งวัน (เช้า)', text: 'ลางานครึ่งวัน (เช้า)' },
  // { value: 'ลางานครึ่งวัน (บ่าย)', text: 'ลางานครึ่งวัน (บ่าย)' },
  { value: 'ขออนุญาตออกนอกสถานที่', text: 'ขออนุญาตออกนอกสถานที่' },
  // {
  //   value: 'ประสบภัย เช่น น้ำท่วม มีพายุ ประสบอุบัติเหตุ',
  //   text: 'ประสบภัย เช่น น้ำท่วม มีพายุ ประสบอุบัติเหตุ',
  // },
  {
    value: 'ปฏิบัติงานในจุดบริการด่วนมหานคร',
    text: 'ปฏิบัติงานในจุดบริการด่วนมหานคร',
  },

  { value: 'อื่นๆ', text: 'อื่นๆ' },
])

/** function เลือกสถานที่*/
function selectLocation() {
  if (model.value === 'อื่นๆ') {
    useLocation.value = ''
  } else {
    useLocation.value = model.value
  }
}

/** Camera */
const camera = ref<InstanceType<typeof Camera>>()
const cameraIsOn = ref<boolean>(false)
const img = ref<any>(undefined)
const photoWidth = ref<number>(350)
const photoHeight = ref<number>(350)
const cameraMountKey = ref<number>(0)
const availableCameras = ref<any[]>([])
const currentCameraIndex = ref<number>(0)
const currentCameraType = ref<'front' | 'back' | 'unknown'>('unknown')

/**
 * Detect real iOS/iPadOS devices. Desktop mobile emulation can spoof the
 * iPhone user-agent, so rely on platform + touch capability instead.
 */
const isIOSDevice: boolean = (() => {
  const platform = navigator.platform || ''
  const hasTouchPoints = navigator.maxTouchPoints > 1

  return (
    /iPhone|iPad|iPod/.test(platform) ||
    (platform === 'MacIntel' && hasTouchPoints)
  )
})()

const isAndroidDevice: boolean = /Android/i.test(navigator.userAgent)
/**
 * `preferNativePhotoCapture` starts false on every device — inline camera is
 * always attempted first.  It is set to true only by
 * `switchToNativePhotoCapture()` / `enableNativePhotoCaptureFallback()` when
 * inline capture is proven to be unsupported or has failed.  This covers iOS
 * Safari too: getUserMedia is supported there since iOS 14.3, so we try
 * inline first and fall back gracefully on first failure.
 */
const preferNativePhotoCapture = ref<boolean>(false)
const useNativePhotoCapture = computed(() => preferNativePhotoCapture.value)
const centeredPreviewImageStyle = Object.freeze({
  objectFit: 'cover',
  objectPosition: 'center center',
})
const INLINE_CAMERA_PREVIEW_TIMEOUT_MS = isIOSDevice ? 2500 : 1800
const INLINE_CAMERA_PREVIEW_POLL_MS = 120
const INLINE_CAMERA_REMOUNT_DELAY_MS = 120
const MAX_INLINE_CAMERA_RECOVERY_ATTEMPTS = 1

/** Ref for the hidden file input used as native camera fallback */
const nativePhotoInput = ref<HTMLInputElement | null>(null)
const DEFAULT_NORMALIZED_IMAGE_QUALITY = 0.92

interface NormalizePhotoOptions {
  fileName?: string
  mirrorHorizontally?: boolean
  quality?: number
}

function clearSelectedPhoto() {
  if (img.value) {
    URL.revokeObjectURL(img.value)
    img.value = undefined
  }
  fileImg.value = undefined
}

async function canDecodeImageBlob(blob: Blob) {
  if (!blob.type.startsWith('image/')) {
    return false
  }

  const imageUrl = URL.createObjectURL(blob)

  try {
    const image = await new Promise<HTMLImageElement>((resolve, reject) => {
      const element = new Image()
      element.onload = () => {
        if (element.naturalWidth > 0 && element.naturalHeight > 0) {
          resolve(element)
        } else {
          reject(new Error('Image has invalid dimensions'))
        }
      }
      element.onerror = () => reject(new Error('Image decode failed'))
      element.src = imageUrl
    })

    return image.naturalWidth > 0 && image.naturalHeight > 0
  } catch (error) {
    console.error('Error decoding captured image:', error)
    return false
  } finally {
    URL.revokeObjectURL(imageUrl)
  }
}

async function isValidCapturedImage(blob: Blob) {
  if (blob.size < MIN_CAPTURE_FILE_SIZE_BYTES) {
    return false
  }

  return canDecodeImageBlob(blob)
}

async function loadImageFromBlob(blob: Blob) {
  const imageUrl = URL.createObjectURL(blob)

  try {
    return await new Promise<HTMLImageElement>((resolve, reject) => {
      const image = new Image()
      image.onload = () => resolve(image)
      image.onerror = () => reject(new Error('Image load failed'))
      image.src = imageUrl
    })
  } finally {
    URL.revokeObjectURL(imageUrl)
  }
}

async function normalizePhotoFile(
  source: Blob,
  options: NormalizePhotoOptions = {}
) {
  const fileName =
    options.fileName ??
    (source instanceof File && source.name
      ? source.name
      : `photo_${Date.now()}.jpg`)
  const imageType =
    source.type && source.type.startsWith('image/') ? source.type : 'image/jpeg'

  if (!options.mirrorHorizontally) {
    if (source instanceof File && source.name === fileName) {
      return source
    }

    return new File([source], fileName, { type: imageType })
  }

  const image = await loadImageFromBlob(source)
  const canvas = document.createElement('canvas')
  canvas.width = image.naturalWidth
  canvas.height = image.naturalHeight

  const context = canvas.getContext('2d')
  if (!context) {
    throw new Error('Canvas context unavailable')
  }

  context.translate(canvas.width, 0)
  context.scale(-1, 1)
  context.drawImage(image, 0, 0, canvas.width, canvas.height)

  const normalizedBlob = await new Promise<Blob | null>((resolve) => {
    canvas.toBlob(
      resolve,
      imageType,
      options.quality ?? DEFAULT_NORMALIZED_IMAGE_QUALITY
    )
  })

  if (!normalizedBlob) {
    throw new Error('Failed to normalize image')
  }

  return new File([normalizedBlob], fileName, { type: imageType })
}

async function assignSelectedPhoto(file: File) {
  clearSelectedPhoto()
  fileImg.value = file
  img.value = URL.createObjectURL(file)
}

function openNativePhotoCapture() {
  void stopInlineCamera()

  if (nativePhotoInput.value) {
    nativePhotoInput.value.value = ''
    nativePhotoInput.value.click()
  }
}

function enableNativePhotoCaptureFallback() {
  preferNativePhotoCapture.value = true
  void stopInlineCamera()
  clearSelectedPhoto()
}

function switchToNativePhotoCapture() {
  enableNativePhotoCaptureFallback()
  openNativePhotoCapture()
}

/**
 * Called when the user selects / captures a photo via the native file-input
 * fallback. Keeps the same `img` + `fileImg` state that capturePhoto()
 * sets so the upload flow is identical.
 */
async function onNativePhotoSelected(event: Event) {
  const input = event.target as HTMLInputElement
  const file = input.files?.[0]
  input.value = ''
  if (!file) return

  if (!(await isValidCapturedImage(file))) {
    clearSelectedPhoto()
    messageError($q, '', 'ไม่สามารถอ่านไฟล์รูปภาพได้ กรุณาถ่ายรูปใหม่อีกครั้ง')
    return
  }

  try {
    const normalizedFile = await normalizePhotoFile(file, {
      fileName: file.name,
      mirrorHorizontally: !isIOSDevice,
    })

    if (!(await isValidCapturedImage(normalizedFile))) {
      throw new Error('Normalized image is invalid')
    }

    await assignSelectedPhoto(normalizedFile)
  } catch (error) {
    console.error('Error normalizing native photo:', error)
    clearSelectedPhoto()
    messageError($q, error, 'ไม่สามารถเตรียมรูปภาพได้ กรุณาถ่ายรูปใหม่อีกครั้ง')
  }
}

const intervalId = ref<ReturnType<typeof setInterval> | undefined>(undefined)

/**
 * เริ่มจาก onMounted #1 เช็ค status คิว
 *
 */
async function startChecking() {
  try {
    // showLoader()
    await fetchCheckStatus()
    await fetchCheckTime()
  } catch (error) {
    console.error('Error in startChecking:', error)
    // hideLoader()
  }
}

/**
 * เริ่มจาก onMounted #2 เช็ค status คิว
 *
 */
async function fetchCheckStatus() {
  try {
    const res = await http.get(config.API.checkStatus())
    inQueue.value = res.data.result.inQueue
    if (res.data.result.inQueue) {
      /** inQueue เป็น true  */
      isDisabledCheckTime.value = true
      msgCheckTime.value = 'ระบบกำลังประมวลผล'
      // if (intervalId.value === undefined) {
      //   intervalId.value = setInterval(async () => {
      //     try {
      //       await fetchCheckStatus()
      //     } catch (error) {
      //       console.error('Error in interval fetchCheckStatus:', error)
      //       // หยุด interval ถ้าเกิด error
      //       stopChecking()
      //     }
      //   }, 3000)
      //   console.log('startChecking called, intervalId:', intervalId.value)
      // }
    } else {
      /** inQueue เป็น false  */
      isDisabledCheckTime.value = false
      msgCheckTime.value = ''
      await stopChecking() // หยุดการทำงาน
    }
  } catch (error) {
    console.log('เกิดข้อผิดพลาด', error)
    messageError($q, error)
    stopChecking() // หยุดการทำงาน
  }
}

/** ตัวใหม่ที่เปลี่ยนก่อนเปลี่ยน
 * เริ่มจาก onMounted #3 เช็ค status คิว
 *
 */
async function stopChecking() {
  if (intervalId.value !== undefined) {
    await fetchCheckTime(false) // เรียก fetchCheckTime ก่อนหยุด interval
    clearInterval(intervalId.value)
    intervalId.value = undefined // รีเซ็ตค่า interval

    async function repeatFetch() {
      await fetchCheckTime(false)
      if (statusCheckin.value && checkDate.value === true) {
        setTimeout(repeatFetch, 1000)
      } else {
        checkDate.value = null
        isErr.value = false
      }
    }

    async function repeatFetchOut() {
      await fetchCheckTime(false)
      if (
        isErr.value === false &&
        statusCheckin.value === false &&
        checkDate.value === true
      ) {
        setTimeout(repeatFetchOut, 1000) // เรียกตัวเองซ้ำ
      } else {
        checkDate.value = null
        isErr.value = null
      }
    }

    // ตรวจสอบเงื่อนไขก่อนเริ่มการ fetch ซ้ำ
    if (statusCheckin.value && checkDate.value === true) {
      repeatFetch()
    } else if (
      isErr.value === false &&
      statusCheckin.value === false &&
      checkDate.value === true
    ) {
      repeatFetchOut()
    }
  }
}

function identifyCameraType(label: string): 'front' | 'back' | 'unknown' {
  const lowerLabel = label.toLowerCase()
  if (
    lowerLabel.includes('front') ||
    lowerLabel.includes('user') ||
    lowerLabel.includes('face')
  ) {
    return 'front'
  } else if (
    lowerLabel.includes('back') ||
    lowerLabel.includes('environment') ||
    lowerLabel.includes('rear')
  ) {
    return 'back'
  }
  return 'unknown'
}

function resolveCameraType(
  label: string,
  fallbackType: 'front' | 'back'
): 'front' | 'back' {
  const detectedType = identifyCameraType(label)
  return detectedType === 'unknown' ? fallbackType : detectedType
}

/** function เปิดกล้อง*/
async function openCamera() {
  // เช็คสิทธิ์ privacy ก่อนเปิดกล้อง
  if (!checkPrivacyAccepted()) {
    return
  }

  // If inline capture previously failed on this device, fall back to the
  // native still-photo picker instead of reopening the live camera.
  if (useNativePhotoCapture.value) {
    openNativePhotoCapture()
    return
  }

  if (cameraIsOn.value) {
    await stopInlineCamera()
    return
  }

  if (
    (isIOSDevice || isAndroidDevice) &&
    !useNativePhotoCapture.value &&
    (cameraPermissionState.value === 'denied' ||
      cameraPermissionState.value === 'unsupported')
  ) {
    switchToNativePhotoCapture()
    return
  }

  const hasCameraPermission = await requestCameraPermission()
  if (!hasCameraPermission) {
    if ((isIOSDevice || isAndroidDevice) && !useNativePhotoCapture.value) {
      switchToNativePhotoCapture()
      return
    }

    messageError(
      $q,
      '',
      'ไม่สามารถเข้าถึงกล้องได้ กรุณาอนุญาตการเข้าถึงกล้องในเบราว์เซอร์ของคุณ'
    )
    return
  }

  try {
    await startInlineCamera()
  } catch (error) {
    console.error('Error opening camera:', error)

    if ((isIOSDevice || isAndroidDevice) && !useNativePhotoCapture.value) {
      switchToNativePhotoCapture()
      return
    }

    messageError($q, error, 'ไม่สามารถเปิดกล้องได้ กรุณาลองใหม่อีกครั้ง')
  }
}

/** change camera device*/
async function changeCamera(targetCameraType?: 'front' | 'back') {
  try {
    const devices: any = await camera.value?.devices(['videoinput'])

    if (!devices || devices.length === 0) {
      console.warn('No cameras found')
      return
    }

    availableCameras.value = devices

    if (devices.length === 1 || !targetCameraType) {
      const device = devices[0]
      const fallbackType =
        targetCameraType ??
        (currentCameraType.value === 'back' ? 'back' : 'front')
      await camera.value?.changeCamera(device.deviceId)
      await ensureInlineCameraPreviewReady()
      currentCameraIndex.value = 0
      currentCameraType.value = resolveCameraType(
        device.label || '',
        fallbackType
      )
      return
    }

    const matchingCameras = devices.filter(
      (device: any) =>
        identifyCameraType(device.label || '') === targetCameraType
    )

    if (matchingCameras.length > 0) {
      const targetDevice = matchingCameras[0]
      await camera.value?.changeCamera(targetDevice.deviceId)
      await ensureInlineCameraPreviewReady()
      currentCameraIndex.value = devices.indexOf(targetDevice)
      currentCameraType.value = resolveCameraType(
        targetDevice.label || '',
        targetCameraType
      )
    } else {
      const nextIndex = (currentCameraIndex.value + 1) % devices.length
      const nextDevice = devices[nextIndex]
      await camera.value?.changeCamera(nextDevice.deviceId)
      await ensureInlineCameraPreviewReady()
      currentCameraIndex.value = nextIndex
      currentCameraType.value = resolveCameraType(
        nextDevice.label || '',
        targetCameraType
      )
    }
  } catch (error) {
    console.error('Error switching camera:', error)
  }
}

/** switch camera device*/
async function switchCamera() {
  if (availableCameras.value.length <= 1) {
    return
  }

  // สลับแค่ระหว่างกล้อง 2 ตัวแรก (กล้องหน้าและหลังหลัก)
  const targetIndex = currentCameraIndex.value === 0 ? 1 : 0
  const targetDevice = availableCameras.value[targetIndex]
  const fallbackType = currentCameraType.value === 'back' ? 'front' : 'back'

  await camera.value?.changeCamera(targetDevice.deviceId)
  await ensureInlineCameraPreviewReady()
  currentCameraIndex.value = targetIndex
  currentCameraType.value = resolveCameraType(
    targetDevice.label || '',
    fallbackType
  )
}

/** function ถ่ายรูป*/
async function capturePhoto() {
  try {
    const imageBlob: any = await camera.value?.snapshot(
      { width: photoWidth.value, height: photoHeight.value },
      'image/jpeg',
      0.8
    )
    if (!imageBlob || !(await isValidCapturedImage(imageBlob))) {
      if (isIOSDevice || isAndroidDevice) {
        switchToNativePhotoCapture()
        return
      }

      messageError($q, '', 'ไม่สามารถถ่ายรูปได้ กรุณาลองใหม่อีกครั้ง')
      return
    }
    const fileName = `photo_${Date.now()}.jpg`
    const normalizedFile = await normalizePhotoFile(imageBlob, {
      fileName,
      mirrorHorizontally: false,
      quality: 0.8,
    })

    if (!(await isValidCapturedImage(normalizedFile))) {
      throw new Error('Normalized image is invalid')
    }

    await assignSelectedPhoto(normalizedFile)

    //แสดงรูป
    await stopInlineCamera()
  } catch (error) {
    console.error('Error capturing photo:', error)

    if ((isIOSDevice || isAndroidDevice) && !useNativePhotoCapture.value) {
      switchToNativePhotoCapture()
      return
    }

    messageError($q, error, 'ไม่สามารถถ่ายรูปได้ กรุณาลองใหม่อีกครั้ง')
  }
}

/** function เปลี่ยนรูปภาพ*/
async function refreshPhoto() {
  try {
    clearSelectedPhoto()

    if (useNativePhotoCapture.value) {
      openNativePhotoCapture()
      return
    }
    await startInlineCamera()
  } catch (error) {
    console.error('Error refreshing photo:', error)
    messageError($q, error, 'ไม่สามารถเปิดกล้องได้ กรุณาลองใหม่อีกครั้ง')
  }
}

/** ref validate*/
const useLocationRef = ref<object | null>(null)
const modelRef = ref<object | null>(null)
const objectRef: FormRef = {
  model: modelRef,
  useLocation: useLocationRef,
}

/** function ตรวจสอบค่าว่างของ input*/
async function validateForm() {
  disabledBtn.value = true

  const hasError = []
  for (const key in objectRef) {
    if (Object.prototype.hasOwnProperty.call(objectRef, key)) {
      const property = objectRef[key]
      if (property.value && typeof property.value.validate === 'function') {
        const isValid = property.value.validate()
        hasError.push(isValid)
      }
    }
  }
  if (hasError.every((result) => result === true)) {
    if (statusCheckin.value == false) {
      getCheck()
    } else if (statusCheckin.value) {
      // dialog ยืนยันการลงเวลาเข้างาน
      dialogConfirm(
        $q,
        () => confirm(),
        'ยืนยันการลงเวลาเข้างาน',
        `แจ้งเตือนการลงเวลาเข้างาน  ${
          workplace.value === 'in-place'
            ? 'ในสถานที่'
            : `นอกสถานที่ (${
                model.value === 'อื่นๆ' ? useLocation.value : model.value
              })`
        } คุณต้องการยืนยันการลงเวลาเข้างาน?`,
        () => {
          disabledBtn.value = false
        },
        'red',
        'ยืนยัน'
      )
    }
  } else {
    disabledBtn.value = false
  }
}

const mapRefDesktop = ref<InstanceType<typeof MapCheck> | null>(null)
const mapRefMobile = ref<InstanceType<typeof MapCheck> | null>(null)

// Computed ref that returns the correct map ref based on screen size
const mapRef = computed(() =>
  $q.screen.gt.xs ? mapRefDesktop.value : mapRefMobile.value
)

const timeChickin = ref<string>('') //เวลาเข้างาน,เวลาออกงาน
const displayedCheckDate = ref<Date>(new Date())

/** function ยืนยันการลงเวลาเข้า - ออก*/
async function confirm() {
  // เช็คสิทธิ์ privacy ก่อนใช้งานแผนที่และกล้อง
  if (!checkPrivacyAccepted()) {
    disabledBtn.value = false
    return
  }

  if (!formLocation.POI || !formLocation.lat || !formLocation.lng) {
    disabledBtn.value = false
    mapRef.value?.requestLocationPermission()
    return
  }

  if (!fileImg.value || !(await isValidCapturedImage(fileImg.value))) {
    if ((isIOSDevice || isAndroidDevice) && !useNativePhotoCapture.value) {
      enableNativePhotoCaptureFallback()
    }
    clearSelectedPhoto()
    disabledBtn.value = false
    messageError($q, '', 'รูปภาพไม่สมบูรณ์ กรุณาถ่ายรูปใหม่อีกครั้งก่อนลงเวลา')
    return
  }

  showLoader()
  const isLocation = workplace.value === 'in-place' //*true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง
  const locationName = workplace.value === 'in-place' ? '' : useLocation.value
  const formdata = new FormData()
  formdata.append('lat', formLocation.lat.toString())
  formdata.append('lon', formLocation.lng.toString())
  formdata.append('POI', formLocation.POI)
  formdata.append('isLocation', isLocation.toString())
  formdata.append('locationName', locationName)
  formdata.append('img', fileImg.value)
  formdata.append('remark', remark.value)
  formdata.append('checkInId', checkInId.value)
  await http
    .post(config.API.checkin(), formdata)
    .then(async (res) => {
      const data = await res.data.result
      const dateObject = new Date(data.date)
      displayedCheckDate.value = dateObject
      checkDate.value = data.date ? true : false
      const options: Intl.DateTimeFormatOptions = {
        hour12: false,
        hour: '2-digit',
        minute: '2-digit',
      }
      const timeString = new Intl.DateTimeFormat('en-US', options).format(
        dateObject
      )
      timeChickin.value = timeString
      modalTime.value = true
      remark.value = ''
      disabledBtn.value = false
    })
    .catch((err) => {
      messageError($q, err)
      disabledBtn.value = false
    })
    .finally(() => {
      hideLoader()
    })
}

async function getCheck() {
  if (!formLocation.POI || !formLocation.lat || !formLocation.lng) {
    disabledBtn.value = false
    mapRef.value?.requestLocationPermission()
    return
  }

  showLoader()
  const isSeminar =
    model.value === 'ไปประชุม / อบรม / สัมมนา'
      ? 'S'
      : model.value === 'ปฏิบัติงานในจุดบริการด่วนมหานคร'
      ? 'O'
      : 'N'

  await http
    .get(config.API.checkoutCheck + `/${isSeminar}`)
    .then(async (res) => {
      checkStatus.value = await res.data.result.status.toLocaleUpperCase()
      if (checkStatus.value == 'ABSENT') {
        const options: Intl.DateTimeFormatOptions = {
          hour12: false,
          hour: '2-digit',
          minute: '2-digit',
        }
        const timeVal =
          new Intl.DateTimeFormat('en-US', options).format(
            new Date(res.data.result.serverTime)
          ) ?? ''
        const endTimeAfternoonVal =
          new Intl.DateTimeFormat('en-US', options).format(
            new Date(res.data.result.endTime)
          ) ?? ''

        // dialog ยืนยันการลงเวลาออกงาน
        dialogConfirm(
          $q,
          () => confirm(),
          'ยืนยันการลงเวลาออกงาน',
          `เวลาออกจากงานของคุณคือ ${endTimeAfternoonVal} แต่ขณะนี้เป็นเวลา ${timeVal} น. หากคุณออกจากงานในเวลานี้สถานะการลงเวลาจะเป็น "${res.data.result.statusText}" คุณแน่ใจว่าจะลงเวลาออกงานในตอนนี้ใช่หรือไม่?`,
          () => {
            disabledBtn.value = false
          },
          'red',
          'ยืนยัน'
        )
      } else if (checkStatus.value == 'NORMAL') {
        confirm()
      }
    })
    .catch((e) => {
      disabledBtn.value = false
      messageError($q, e)
    })
    .finally(() => {
      hideLoader()
    })
}
/** ปิด popup แสดงการลงเวลา*/
async function onClickConfirm() {
  try {
    showLoader()
    cameraIsOn.value = false
    clearSelectedPhoto()
    modalTime.value = false
    if (!statusCheckin.value) {
      statusCheckin.value = true
    }
    await startChecking()
  } finally {
    hideLoader()
  }
}

/** เลือกสถานที่ทำงาน*/
function updateWorkplace() {
  useLocation.value = ''
  model.value = ''
}

/**
 *  รี้เทินร์ class สีพื้นหลัง
 * @param val ค่า statusCheckin
 */
const getClass = (val: boolean) => {
  return {
    'bg-primary text-white col-12 row items-center q-px-md q-py-sm ': val,
    'bg-red-8 text-white col-12 row items-center q-px-md q-py-sm ': !val,
  }
}

const getClassXS = (val: boolean) => {
  return {
    'text-white q-pa-lg col-12 row bg-primary': val,
    'text-white q-pa-lg col-12 row bg-red-8': !val,
  }
}
const inQueue = ref<boolean>(false)

// ฟังก์ชันสำหรับรีเซ็ตรูปและหยุดกล้อง
function resetCameraAndImage() {
  clearSelectedPhoto()
  void stopInlineCamera()
}

// เพิ่มฟังก์ชันสำหรับจัดการการปิดแอพในมือถือ
function handleAppClose() {
  resetCameraAndImage()

  // หยุด interval ถ้ามี
  if (intervalId.value !== undefined) {
    clearInterval(intervalId.value)
    intervalId.value = undefined
  }
}

// จัดการ visibility change สำหรับมือถือ
function handleVisibilityChange() {
  if (document.visibilityState === 'hidden') {
    handleAppClose()
  } else if (document.visibilityState === 'visible') {
    // เมื่อกลับมาที่แอพ ให้เรียกแผนที่ใหม่
    if (privacyStore.isAccepted) {
      mapRef.value?.requestLocationPermission()
    }
  }
}

/** Hook*/
onMounted(async () => {
  // เริ่มต้น clock เสมอ
  updateClock()
  clockInterval.value = setInterval(updateClock, 1000)

  // เพิ่ม event listeners สำหรับมือถือ
  document.addEventListener('visibilitychange', handleVisibilityChange)
  window.addEventListener('pagehide', handleAppClose)

  // ถ้ามีข้อมูลสังกัดแล้ว เริ่ม checking ได้เลย
  if (hasOrganization.value) {
    isLoadingCheckTime.value = true
    startChecking()
  }

  await syncPermissionStates()

  // เรียกแผนที่เฉพาะเมื่อยอมรับ privacy แล้ว
  if (privacyStore.isAccepted) {
    mapRef.value?.requestLocationPermission()
  }
})

// เฝ้าดูการเปลี่ยนแปลงของ hasOrganization
// เพื่อเริ่ม startChecking เมื่อมีข้อมูลสังกัด
watch(
  hasOrganization,
  (newValue) => {
    if (newValue && !isLoadingCheckTime.value) {
      isLoadingCheckTime.value = true
      startChecking()
    }
  },
  { immediate: false }
)

onBeforeUnmount(() => {
  resetCameraAndImage()

  // หยุด clock interval
  if (clockInterval.value !== undefined) {
    clearInterval(clockInterval.value)
    clockInterval.value = undefined
  }

  // หยุด interval ถ้ามี
  if (intervalId.value !== undefined) {
    clearInterval(intervalId.value)
    intervalId.value = undefined
  }

  // ลบ event listeners
  document.removeEventListener('visibilitychange', handleVisibilityChange)
  window.removeEventListener('pagehide', handleAppClose)
})

watch(
  () => privacyStore.isAccepted,
  async (newVal) => {
    if (newVal) {
      await syncPermissionStates()
      mapRef.value?.requestLocationPermission()
    }
  }
)

/** Watch notification counter on socket */
watch(notificationCounter, () => {
  startChecking()
})
</script>

<template>
  <q-page :style="$q.screen.xs ? 'padding-top: 90px' : ''">
    <div class="col-12 row justify-center">
      <div class="col-xs-12 col-sm-12 col-md-12">
        <q-card
          flat
          :class="
            $q.screen.gt.xs ? 'row col-12 cardNone' : 'row col-12 bg-grey-2'
          "
        >
          <!-- <q-header elevated class="bg-purple"> -->
          <div
            class="col-12 q-pa-md items-center gt-xs"
            v-if="isLoadingCheckTime"
          >
            <q-skeleton width="100%" height="50px" />
          </div>

          <div v-else :class="getClass(statusCheckin)" class="gt-xs">
            <div class="col">
              <div class="row col-12 justify-center q-py-sm text-subtitle1">
                <strong v-if="!statusCheckin && inQueue">
                  ลงเวลาออกงาน (ระบบกำลังประมวลผล)
                </strong>

                <strong v-else-if="statusCheckin && inQueue">
                  ลงเวลาเข้างาน (ระบบกำลังประมวลผล)
                </strong>

                <strong v-else-if="statusCheckin && !inQueue">
                  ลงเวลาเข้างาน
                </strong>

                <strong v-else> ลงเวลาออกงาน </strong>
              </div>
            </div>
          </div>

          <div class="col-12 text-grey-9">
            <div class="col-12 row justify-center">
              <div class="col-12 row q-pt-md justify-center gt-xs">
                <div
                  class="col-xs-12 col-sm-10 text-h6 text-center text-weight-bold"
                >
                  {{ date2Thai(Thai) }}
                </div>
                <div class="row col-12 justify-center q-py-sm">
                  <div class="colunm">
                    <div class="text-h3 text-weight-bold">
                      {{ formattedH }}<span class="q-ma-md">:</span>
                    </div>
                  </div>
                  <div class="colunm">
                    <div class="text-h3 text-weight-bold">
                      {{ formattedM }}<span class="q-ma-md">:</span>
                    </div>
                  </div>
                  <div class="colunm">
                    <div class="text-h3 text-weight-bold">{{ formattedS }}</div>
                  </div>
                </div>
              </div>

              <div v-if="isDisabledCheckTime && msgCheckTime">
                <div class="col-12 column items-center justify-center q-pa-xl">
                  <q-icon
                    name="mdi-map-marker-radius"
                    size="8em"
                    class="text-primary q-mb-md"
                  />
                  <div class="text-h6 text-center text-red q-mt-sm">
                    *{{ msgCheckTime }}
                  </div>
                </div>
              </div>

              <div
                class="col-xs-12 col-md-11 q-pa-md q-col-gutter-md row q-pt-lg"
                v-if="!isDisabledCheckTime"
              >
                <div v-if="crossDayCheckoutWarning" class="col-12">
                  <q-banner
                    rounded
                    class="bg-white text-orange-10 warning-banner"
                  >
                    <q-icon name="warning" color="warning" size="20px" />

                    {{ crossDayCheckoutWarning }}
                  </q-banner>
                </div>
                <div class="col-xs-12 col-sm-8 gt-xs">
                  <div class="col-12">
                    <MapCheck
                      ref="mapRefDesktop"
                      :initialPOI="formLocation.POI"
                      @update:location="updateLocation"
                    />
                  </div>
                </div>
                <div v-if="$q.screen.gt.xs" class="col-xs-12 col-sm-4">
                  <q-card
                    :class="
                      $q.screen.xs ? 'card-container-xs' : 'card-container'
                    "
                  >
                    <div
                      v-if="!cameraIsOn && img == null"
                      class="preview-placeholder"
                      @click="() => !isDisabledCheckTime && openCamera()"
                    >
                      <div class="text-center">
                        <q-icon
                          name="mdi-camera"
                          color="blue-grey-3"
                          size="100px"
                          class="center-icon"
                        />
                      </div>
                    </div>
                    <div class="col-12 row items-center">
                      <!-- แสดงกล้องตอนกดถ่ายภาพ -->
                      <Camera
                        :key="`desktop-${cameraMountKey}`"
                        :resolution="{ width: photoWidth, height: photoHeight }"
                        ref="camera"
                        :class="['camera-preview']"
                        :autoplay="false"
                        :playsinline="true"
                        :facingMode="'user'"
                      />

                      <!-- แสดงรูปเมื่อกด capture -->
                      <div v-if="img" class="image-container">
                        <q-img
                          :src="img"
                          class="image-element"
                          :img-style="centeredPreviewImageStyle"
                        ></q-img>
                        <!-- Native capture: cameraIsOn stays false so show retake here
                             the refresh button here instead of inside v-if="cameraIsOn" -->
                        <div
                          v-if="useNativePhotoCapture"
                          class="absolute-bottom-right q-ma-md"
                        >
                          <q-btn
                            round
                            push
                            icon="refresh"
                            size="md"
                            color="negative"
                            @click="refreshPhoto"
                          />
                        </div>
                      </div>

                      <div v-if="cameraIsOn && img == null">
                        <div
                          v-if="$q.screen.gt.xs"
                          class="absolute-bottom-right q-ma-md"
                        >
                          <q-btn
                            v-if="availableCameras.length > 1"
                            round
                            push
                            icon="flip_camera_ios"
                            size="sm"
                            color="secondary"
                            class="q-mr-sm"
                            @click="switchCamera"
                          />
                          <q-btn
                            round
                            push
                            icon="photo_camera"
                            size="md"
                            color="positive"
                            @click="capturePhoto"
                          />
                        </div>
                        <div v-else>
                          <div
                            class="absolute-bottom text-subtitle2 text-center q-py-sm"
                            style="background: #00000021"
                          >
                            <q-btn
                              v-if="availableCameras.length > 1"
                              round
                              icon="flip_camera_ios"
                              size="16px"
                              style="background: #424242; color: white"
                              @click="switchCamera"
                              unelevated
                              class="q-mr-xs"
                            />
                            <q-btn
                              round
                              icon="photo_camera"
                              size="18px"
                              style="background: #263238; color: white"
                              @click="capturePhoto"
                              unelevated
                            />
                          </div>
                        </div>
                      </div>

                      <!-- Refresh button - shows when photo is captured regardless of camera state -->
                      <div v-if="img != null && !useNativePhotoCapture">
                        <div
                          v-if="$q.screen.gt.xs"
                          class="absolute-bottom-right q-ma-md"
                        >
                          <q-btn
                            round
                            push
                            icon="refresh"
                            size="md"
                            color="negative"
                            @click="refreshPhoto"
                          />
                        </div>
                        <div v-else>
                          <div
                            class="absolute-bottom text-subtitle2 text-center q-py-sm"
                            style="background: #00000021"
                          >
                            <q-btn
                              round
                              icon="refresh"
                              size="18px"
                              style="background: #263238; color: white"
                              @click="refreshPhoto"
                              unelevated
                            />
                          </div>
                        </div>
                      </div>
                    </div>
                  </q-card>
                </div>

                <!-- กรอกข้อมูล หน้ามือถือ -->
                <div class="col-12 row q-col-gutter-y-md" v-if="$q.screen.xs">
                  <!-- กล้อง หน้ามือถือ -->
                  <div class="col-12">
                    <q-card
                      :class="
                        $q.screen.xs ? 'card-container-xs' : 'card-container'
                      "
                    >
                      <div
                        v-if="!cameraIsOn && img == null"
                        class="preview-placeholder"
                        @click="() => !isDisabledCheckTime && openCamera()"
                      >
                        <div class="text-center">
                          <q-icon
                            name="mdi-camera"
                            color="blue-grey-3"
                            size="100px"
                            class="center-icon"
                          />
                        </div>
                      </div>
                      <div class="col-12 row items-center">
                        <!-- แสดงกล้องตอนกดถ่ายภาพ -->
                        <Camera
                          :key="`mobile-${cameraMountKey}`"
                          :resolution="{
                            width: photoWidth,
                            height: photoHeight,
                          }"
                          ref="camera"
                          :class="['camera-preview']"
                          :autoplay="false"
                          :playsinline="true"
                          :facingMode="'user'"
                        />

                        <!-- แสดงรูปเมื่อกด capture -->
                        <div v-if="img" class="image-container">
                          <q-img
                            :src="img"
                            class="image-element"
                            :img-style="centeredPreviewImageStyle"
                          ></q-img>
                          <!-- Native capture: cameraIsOn stays false so show retake here
                               the refresh button here instead of inside v-if="cameraIsOn" -->
                          <div
                            v-if="useNativePhotoCapture"
                            class="absolute-bottom text-subtitle2 text-center q-py-sm"
                            style="background: #00000021"
                          >
                            <q-btn
                              round
                              icon="refresh"
                              size="18px"
                              style="background: #263238; color: white"
                              @click="refreshPhoto"
                              unelevated
                            />
                          </div>
                        </div>

                        <div v-if="cameraIsOn && img == null">
                          <div
                            class="absolute-bottom text-subtitle2 text-center q-py-sm"
                            style="background: #00000021"
                          >
                            <q-btn
                              v-if="availableCameras.length > 1"
                              round
                              icon="flip_camera_ios"
                              size="16px"
                              style="background: #424242; color: white"
                              @click="switchCamera"
                              unelevated
                              class="q-mr-xs"
                            />
                            <q-btn
                              round
                              icon="photo_camera"
                              size="18px"
                              style="background: #263238; color: white"
                              @click="capturePhoto"
                              unelevated
                            />
                          </div>
                        </div>

                        <!-- Refresh button - shows when photo is captured regardless of camera state -->
                        <div v-if="img != null && !useNativePhotoCapture">
                          <div
                            class="absolute-bottom text-subtitle2 text-center q-py-sm"
                            style="background: #00000021"
                          >
                            <q-btn
                              round
                              icon="refresh"
                              size="18px"
                              style="background: #263238; color: white"
                              @click="refreshPhoto"
                              unelevated
                            />
                          </div>
                        </div>
                      </div>
                    </q-card>
                  </div>

                  <div class="col-12" v-if="!isDisabledCheckTime">
                    <q-card
                      flat
                      bordered
                      class="row col-12"
                      style="border-radius: 10px"
                    >
                      <q-input
                        v-model="remark"
                        filled
                        label="กรอกหมายเหตุ"
                        input-style="border-radius: 10px 10px 0 0;"
                        class="col-12"
                        bg-color="white"
                      />
                      <div class="col-12"><q-separator /></div>
                      <q-btn-toggle
                        v-model="workplace"
                        @update:model-value="updateWorkplace"
                        spread
                        no-caps
                        toggle-color="blue-grey-10"
                        color="white"
                        text-color="black"
                        class="col-12"
                        style="min-height: 3em"
                        size="15px"
                        :options="[
                          { label: 'ในสถานที่', value: 'in-place' },
                          { label: 'นอกสถานที่', value: 'off-site' },
                        ]"
                      />
                    </q-card>
                  </div>
                  <div class="col-12" v-if="workplace == 'off-site'">
                    <q-card class="col-12">
                      <q-select
                        ref="modelRef"
                        filled
                        v-model="model"
                        :options="options"
                        label="เลือกสถานที่"
                        bg-color="white"
                        :rules="[(val:string) => !!val || 'กรุณาระบุสถานที่']"
                        lazy-rules
                        @update:model-value="selectLocation()"
                        hide-bottom-space
                        option-value="value"
                        option-label="text"
                        emit-value
                        map-options
                      />
                    </q-card>
                  </div>
                  <div
                    class="col-12"
                    v-if="model == 'อื่นๆ' && workplace === 'off-site'"
                  >
                    <q-card class="col-12">
                      <q-input
                        ref="useLocationRef"
                        filled
                        v-model="useLocation"
                        label="ระบุสถานที่"
                        bg-color="white"
                        :rules="[(val:string) => !!val || 'กรุณาระบุสถานที่']"
                        hide-bottom-space
                        lazy-rules
                      />
                    </q-card>
                  </div>

                  <!-- Map หน้ามือถือ -->
                  <div class="col-12">
                    <MapCheck
                      ref="mapRefMobile"
                      :initialPOI="formLocation.POI"
                      @update:location="updateLocation"
                    />
                  </div>
                </div>
                <!-- กรอกข้อมูล หน้ามือถือ -->

                <div
                  v-if="!isDisabledCheckTime"
                  class="col-xs-12 col-sm-12 items-center gt-xs"
                >
                  <q-card
                    bordered
                    flat
                    :class="
                      $q.screen.gt.xs
                        ? 'q-px-md q-py-sm row items-center  shadow-0'
                        : 'q-pa-md row items-center  shadow-0'
                    "
                  >
                    <div class="text-weight-bold">สถานที่ทำงาน</div>
                    <div
                      :class="
                        $q.screen.gt.xs
                          ? 'row q-gutter-md q-pl-md col-sm-6 col-md-3'
                          : 'column col-12'
                      "
                    >
                      <q-radio
                        v-model="workplace"
                        checked-icon="task_alt"
                        unchecked-icon="panorama_fish_eye"
                        val="in-place"
                        label="ในสถานที่"
                        @update:model-value="updateWorkplace"
                      />
                      <q-radio
                        v-model="workplace"
                        checked-icon="task_alt"
                        unchecked-icon="panorama_fish_eye"
                        val="off-site"
                        label="นอกสถานที่"
                        @update:model-value="updateWorkplace"
                      />
                    </div>
                    <div class="col-sm-12 col-md-8">
                      <div class="row items-top">
                        <div
                          class="col-xs-12 col-sm-6 col-md-6"
                          v-if="workplace == 'off-site'"
                        >
                          <q-select
                            ref="modelRef"
                            dense
                            class="q-ml-md"
                            outlined
                            v-model="model"
                            :options="options"
                            prefix="ระบุสถานที่ :"
                            :rules="[(val:string) => !!val || 'กรุณาระบุสถานที่']"
                            lazy-rules
                            @update:model-value="selectLocation()"
                            hide-bottom-space
                            option-value="value"
                            option-label="text"
                            emit-value
                            map-options
                          />
                        </div>
                        <div
                          class="col-xs-12 col-sm-6 col-md-6"
                          v-if="model == 'อื่นๆ' && workplace === 'off-site'"
                        >
                          <q-input
                            ref="useLocationRef"
                            dense
                            class="q-ml-md"
                            outlined
                            v-model="useLocation"
                            label="ระบุสถานที่"
                            hide-bottom-space
                            :rules="[(val:string) => !!val || 'กรุณาระบุสถานที่']"
                            lazy-rules
                          />
                        </div>
                      </div>
                    </div>
                  </q-card>
                </div>

                <div
                  v-if="!isDisabledCheckTime"
                  class="col-xs-12 col-sm-12 gt-xs"
                >
                  <q-card
                    bordered
                    flat
                    :class="
                      $q.screen.gt.xs
                        ? 'q-px-md q-py-sm row items-center  shadow-0'
                        : 'q-pa-md row items-center  shadow-0'
                    "
                  >
                    <div class="text-weight-bold">หมายเหตุ</div>
                    <div class="col-12 q-pt-sm">
                      <q-input outlined v-model="remark" lazy-rules dense />
                    </div>
                  </q-card>
                </div>
              </div>
            </div>

            <div
              v-if="!isDisabledCheckTime && $q.screen.gt.xs"
              class="col-12 text-right"
            >
              <q-separator />
              <div class="col-12 q-pa-md">
                <!-- <p
                  :class="
                    $q.screen.gt.xs
                      ? 'text-red text-caption '
                      : 'text-red text-caption text-center'
                  "
                >
                  *หมายเหตุ คลิกลงเวลาเข้างานแล้วระบบจะลงเวลาทันที
                </p> -->
                <q-btn
                  :label="
                    !statusCheckin && inQueue
                      ? 'ลงเวลาเข้างาน (ระบบกำลังประมวลผล)'
                      : statusCheckin && inQueue
                      ? 'ลงเวลาออกงาน (ระบบกำลังประมวลผล)'
                      : statusCheckin && !inQueue
                      ? 'ลงเวลาเข้างาน'
                      : 'ลงเวลาออกงาน'
                  "
                  :color="
                    img == null
                      ? 'grey-6'
                      : !statusCheckin && inQueue
                      ? 'primary'
                      : statusCheckin && inQueue
                      ? 'red-8'
                      : statusCheckin && !inQueue
                      ? 'primary'
                      : 'red-8'
                  "
                  push
                  size="18px"
                  :class="$q.screen.gt.xs ? 'q-px-md' : 'full-width q-pa-sm'"
                  :disable="disabledBtn ? true : camera && img ? false : true"
                  @click="validateForm"
                  :loading="inQueue"
                />
              </div>
            </div>

            <!-- <div v-if="isDisabledCheckTime && $q.screen.gt.xs" class="col-12">
              <q-separator />
              <div class="text-red q-pa-md">*{{ msgCheckTime }}</div>
            </div> -->
          </div>
        </q-card>
      </div>
    </div>
  </q-page>

  <!--  top page sticky  หน้ามือถือ-->
  <q-page-sticky expand position="top" v-if="$q.screen.xs">
    <div
      class="row q-col-gutter-sm col bg-white col-12 q-pa-md"
      style="height: 100px"
      v-if="isLoadingCheckTime"
    >
      <div class="col-7">
        <div class="row col-12">
          <q-skeleton type="text" width="90%" />
          <q-skeleton type="text" width="50%" class="text-subtitle1" />
        </div>
      </div>
      <div class="col-5">
        <div class="row col-12">
          <q-skeleton type="QBtn" width="100%" />
        </div>
      </div>
    </div>
    <div v-else :class="getClassXS(statusCheckin)">
      <div class="col">
        <div class="row col-12 justify-right items-center text-subtitle1">
          <strong v-if="!statusCheckin && inQueue">
            ลงเวลาออกงาน (ระบบกำลังประมวลผล)
          </strong>

          <strong v-else-if="statusCheckin && inQueue">
            ลงเวลาเข้างาน (ระบบกำลังประมวลผล)
          </strong>

          <strong v-else-if="statusCheckin && !inQueue"> ลงเวลาเข้างาน </strong>

          <strong v-else> ลงเวลาออกงาน </strong>
        </div>
        <div
          class="text-white text-subtitle1 row col-12"
          style="line-height: 1.5rem"
        >
          {{ date2Thai(Thai) }}
        </div>
      </div>
      <div class="col-6 row items-center">
        <div class="row col-12 justify-end">
          <div class="colunm">
            <div class="text-h4 text-weight-bold">
              {{ formattedH }}<span class="q-ma-xs">:</span>
            </div>
          </div>
          <div class="colunm">
            <div class="text-h4 text-weight-bold">
              {{ formattedM }}<span class="q-ma-xs">:</span>
            </div>
          </div>
          <div class="colunm">
            <div class="text-h4 text-weight-bold">{{ formattedS }}</div>
          </div>
        </div>
      </div>
    </div>
  </q-page-sticky>

  <!--  footer  หน้ามือถือ-->
  <q-footer reveal v-if="$q.screen.xs" class="bg-grey-2">
    <q-separator />
    <div v-if="!isDisabledCheckTime" class="col-12 q-pa-md">
      <!-- <p
        :class="
          $q.screen.gt.xs
            ? 'text-red text-caption '
            : 'text-red text-caption text-center'
        "
      >
        *หมายเหตุ คลิกลงเวลาเข้างานแล้วระบบจะลงเวลาทันที
      </p> -->
      <q-btn
        :label="
          !statusCheckin && inQueue
            ? 'ลงเวลาเข้างาน (ระบบกำลังประมวลผล)'
            : statusCheckin && inQueue
            ? 'ลงเวลาออกงาน (ระบบกำลังประมวลผล)'
            : statusCheckin && !inQueue
            ? 'ลงเวลาเข้างาน'
            : 'ลงเวลาออกงาน'
        "
        :color="
          img == null
            ? 'grey-6'
            : !statusCheckin && inQueue
            ? 'primary'
            : statusCheckin && inQueue
            ? 'red-8'
            : statusCheckin && !inQueue
            ? 'primary'
            : 'red-8'
        "
        push
        size="18px"
        :class="$q.screen.gt.xs ? 'q-px-md' : 'full-width q-pa-sm'"
        :disable="disabledBtn ? true : camera && img ? false : true"
        @click="validateForm"
        :loading="inQueue"
      />
    </div>
    <!-- <div v-if="isDisabledCheckTime" class="col-12">
      <q-separator />
      <div class="text-red q-pa-md text-left">*{{ msgCheckTime }}</div>
    </div> -->
  </q-footer>

  <!-- แสดงการลงเวลา -->
  <q-dialog v-model="modalTime" persistent>
    <q-card class="full-width cardNone">
      <div class="col-12 row items-center q-px-md q-py-sm bg-grey-2">
        <div class="text-body1 text-center col-12 text-weight-bold">
          <span v-if="statusCheckin">ลงเวลาเข้างานของคุณ</span>
          <span v-else>ลงเวลาออกงานของคุณ</span>
        </div>
      </div>
      <q-card-section class="row col-12 justify-center">
        <div
          class="rounded-borders q-pa-md col-11"
          :class="
            !statusCheckin && inQueue
              ? 'bg-primary'
              : statusCheckin && inQueue
              ? 'bg-red-8'
              : statusCheckin && !inQueue
              ? 'bg-primary'
              : 'bg-red-8'
          "
        >
          <div
            class="col-12 text-subtitle1 text-center text-white text-weight-medium"
          >
            {{ date2Thai(displayedCheckDate) }}
          </div>
          <div class="row col-12 justify-center q-pt-sm">
            <!-- <div class="text-h3 text-white text-weight-bold"></div> -->
            <div class="text-h3 text-white text-weight-bold">
              {{ timeChickin }}
            </div>
          </div>
        </div>
        <div class="col-12 text-center row q-pt-md">
          <div class="col-12 text-subtitle1 text-weight-medium text-secondary">
            พื้นที่ใกล้เคียง {{ formLocation.POI }}
          </div>
          <div class="col-12 text-subtitle1 text-weight-medium text-secondary">
            {{ location }}
          </div>
          <div class="col-12 text-grey-7">
            {{ formLocation.lat }} , {{ formLocation.lng }}
          </div>
        </div>
      </q-card-section>

      <q-card-actions align="center" class="q-mb-md row">
        <q-btn
          class="col-xs-11 col-sm-6"
          push
          label="ตกลง"
          color="secondary"
          v-close-popup
          @click="onClickConfirm"
        />
      </q-card-actions>
    </q-card>
  </q-dialog>

  <!--
    Native still-image fallback: hidden file input.
    Activated only after inline camera is proven unsupported or fails — on any
    platform including iOS.  Never used as the first-choice path.
  -->
  <input
    ref="nativePhotoInput"
    type="file"
    accept="image/*"
    capture="user"
    style="display: none"
    @change="onNativePhotoSelected"
  />
</template>

<style scoped>
.q-card.cardImg:hover {
  border: 1px solid #02a998 !important;
}

.center-icon {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}
.card-container {
  position: relative;
  overflow: hidden;
  height: auto; /* Adjust as needed */
  background: #f5f5f5;
  height: 35vh !important;
  box-shadow: none !important;
  border: 1px solid #ededed;
}
.card-container-xs {
  position: relative;
  overflow: hidden;
  height: auto; /* Adjust as needed */
  background: #ffffff;
  height: 35vh !important;
  border-radius: 10px;
}
.image-container {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
  border-radius: 8px;
}

.image-element {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 8px; /* Adjust as needed */
}

.preview-placeholder {
  width: 100%;
  height: 100%;
}

/* Keep the live camera preview visually aligned with the normalized saved photo. */
.camera-preview :deep(video) {
  width: 100%;
  height: 100%;
  object-fit: cover;
  -webkit-object-fit: cover;
}

.bg-topOut {
  background: rgb(39, 50, 56);
  background: linear-gradient(
    175deg,
    rgba(39, 50, 56, 1) 76%,
    rgba(198, 40, 40, 1) 100%
  );
}

.bg-topIn {
  background: rgb(39, 50, 56);
  background: linear-gradient(
    175deg,
    rgba(39, 50, 56, 1) 76%,
    rgba(2, 169, 152, 1) 100%
  );
}

.icon-bounce {
  animation: bounce 2s infinite;
}

.warning-banner {
  border: 1px solid #ffcc80;
  border-left: 4px solid #fb8c00;
}

@keyframes bounce {
  0%,
  100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-10px);
  }
}
</style>
