import os
from uuid import uuid4
from sqlalchemy.orm import Session
from fastapi import UploadFile
from app.crud import checkin_ftth_new as crud
from app.models.near_me import FTTH_DETAIL_FAT_NEAR_ME

class CheckinService:
    @staticmethod
    def create_pelanggan_record(db3: Session, form_data: dict, kode_sales: str) -> dict:
        # Generate no_activity
        no_activity = crud.generate_no_activity(db3, kode_sales)
        
        # Prepare data dict
        data = form_data.copy()
        data["no_activity"] = no_activity
        data["kode_sales"] = kode_sales
        
        # Create record
        pelanggan = crud.create_data_pelanggan(db3, data)
        
        return {
            "id": pelanggan.id,
            "no_activity": pelanggan.no_activity,
            "nama_pelanggan": pelanggan.nama_pelanggan,
            "nomor_pelanggan": pelanggan.nomor_pelanggan,
            "latitude_rumah": float(pelanggan.latitude_rumah),
            "longitude_rumah": float(pelanggan.longitude_rumah),
            "keterangan": pelanggan.keterangan,
            "kode_sales": pelanggan.kode_sales,
            "status": pelanggan.status,
            "tanggal": pelanggan.tanggal,
        }

    @staticmethod
    def get_history_list(db3: Session, kode_sales: str) -> list[dict]:
        history = []
        
        # 1. Fetch pending list
        pending_list = crud.get_pending_list(db3, kode_sales)
        for p in pending_list:
            history.append({
                "id": p.id,
                "no_activity": p.no_activity,
                "nama_pelanggan": p.nama_pelanggan,
                "tanggal": p.tanggal,
                "form_status": "pending",
                "status_pemesanan": "-",
                "kode_sales": "-"
            })
            
        # 2. Fetch done list
        done_list = crud.get_done_list(db3, kode_sales)
        for d in done_list:
            history.append({
                "id": d.id,
                "no_activity": d.no_activity,
                "nama_pelanggan": d.nama_pelanggan,
                "tanggal": d.tanggal_checkin,  # For done items, we sort and display by checkin date
                "form_status": "done",
                "status_pemesanan": d.status_pemesanan,
                "kode_sales": d.kode_sales
            })
            
        # Sort combined list by date (newest first)
        history.sort(
            key=lambda x: x["tanggal"],
            reverse=True
        )
        return history

    @staticmethod
    def get_pending_detail(db3: Session, no_activity: str, kode_sales: str) -> dict:
        pending = crud.get_pending_by_no_activity(db3, no_activity, kode_sales)
        if not pending:
            raise ValueError(f"Data pending dengan no_activity {no_activity} tidak ditemukan.")
            
        return {
            "id": pending.id,
            "no_activity": pending.no_activity,
            "nama_pelanggan": pending.nama_pelanggan,
            "nomor_pelanggan": pending.nomor_pelanggan,
            "latitude_rumah": float(pending.latitude_rumah),
            "longitude_rumah": float(pending.longitude_rumah),
            "keterangan": pending.keterangan,
            "kode_sales": pending.kode_sales,
            "status": pending.status,
            "tanggal": pending.tanggal,
        }

    @staticmethod
    async def create_checkin_from_form2(
        db3: Session,
        db2: Session,
        no_activity: str,
        kode_sales: str,
        form2_data: dict,
        foto_fat: UploadFile
    ) -> dict:
        # 1. Fetch FAT coordinate from db2
        fat = db2.query(FTTH_DETAIL_FAT_NEAR_ME).filter(
            FTTH_DETAIL_FAT_NEAR_ME.KODE == form2_data["fat_code"]
        ).first()
        if not fat:
            raise ValueError("Tiang FAT tidak ditemukan.")
            
        # 2. Calculate distance
        lat_sales = form2_data["latitude_sales"]
        lon_sales = form2_data["longitude_sales"]
        lat_fat = float(fat.LATITUDE)
        lon_fat = float(fat.LONGITUDE)
        
        jarak = crud.validate_radius(lat_sales, lon_sales, lat_fat, lon_fat)
        
        # 3. Validation radius max 15 meters
        if jarak > 25.0:
            raise ValueError(f"Jarak terlalu jauh dari tiang FAT: {jarak:.1f} meter")
            
        # 4. Upload photo
        upload_dir = "uploads/foto_fat"
        os.makedirs(upload_dir, exist_ok=True)
        ext = os.path.splitext(foto_fat.filename)[-1]
        file_name = f"{uuid4().hex}{ext}"
        file_path = os.path.join(upload_dir, file_name)
        
        with open(file_path, "wb") as f:
            f.write(await foto_fat.read())
            
        foto_path = f"/{upload_dir}/{file_name}"
        
        # 5. Populate form2_data with calculations
        data = form2_data.copy()
        data["latitude_fat"] = lat_fat
        data["longitude_fat"] = lon_fat
        data["jarak_meter"] = jarak
        data["foto_fat"] = foto_path
        
        # 6. Remove columns that do not exist in the database model
        data.pop("latitude_sales", None)
        data.pop("longitude_sales", None)
        
        # 7. Merge and create final checkin record
        checkin = crud.merge_and_create_checkin(db3, no_activity, kode_sales, data)
        
        return {
            "id": checkin.id,
            "no_activity": checkin.no_activity,
            "nama_pelanggan": checkin.nama_pelanggan,
            "nomor_pelanggan": checkin.nomor_pelanggan,
            "latitude_rumah": float(checkin.latitude_rumah),
            "longitude_rumah": float(checkin.longitude_rumah),
            "keterangan": checkin.keterangan,
            "fat_code": checkin.fat_code,
            "foto_fat": checkin.foto_fat,
            "latitude_fat": float(checkin.latitude_fat),
            "longitude_fat": float(checkin.longitude_fat),
            "jarak_meter": float(checkin.jarak_meter),
            "status_pemesanan": checkin.status_pemesanan,
            "tanggal_po": checkin.tanggal_po,
            "tanggal_so": checkin.tanggal_so,
            "tanggal_sa": checkin.tanggal_sa,
            "tanggal_pa": checkin.tanggal_pa,
            "kode_sales": checkin.kode_sales,
            "status_checkin": checkin.status_checkin,
            "status_checkin": checkin.status_checkin,
            "tanggal_form1": checkin.tanggal_form1,
            "tanggal_checkin": checkin.tanggal_checkin,
        }

    @staticmethod
    def submit_new_fat(
        db3: Session,
        no_activity: str,
        kode_sales: str,
        payload_data: dict
    ) -> dict:
        # Calculate slots based on tipe_fat
        tipe_fat = payload_data.get("tipe_fat", "")
        if "16" in tipe_fat:
            slot_available = 15
            slot_occupied = 1
        elif "24" in tipe_fat:
            slot_available = 23
            slot_occupied = 1
        else:
            # Default fallback
            slot_available = 7
            slot_occupied = 1
            
        fat_code = crud.generate_fat_code(db3)
        
        new_fat_data = {
            "fat_code": fat_code,
            "tipe_fat": tipe_fat,
            "longitude_fat": payload_data["longitude_fat"],
            "latitude_fat": payload_data["latitude_fat"],
            "slot_available": slot_available,
            "slot_occupied": slot_occupied,
            "status_pemesanan": payload_data["status_pemesanan"]
        }
        
        checkin, fat_baru = crud.merge_and_create_checkin_new_fat(
            db3=db3,
            no_activity=no_activity,
            kode_sales=kode_sales,
            new_fat_data=new_fat_data
        )
        
        return {
            "checkin": {
                "id": checkin.id,
                "no_activity": checkin.no_activity,
                "nama_pelanggan": checkin.nama_pelanggan,
                "nomor_pelanggan": checkin.nomor_pelanggan,
                "latitude_rumah": float(checkin.latitude_rumah),
                "longitude_rumah": float(checkin.longitude_rumah),
                "keterangan": checkin.keterangan,
                "fat_code": checkin.fat_code,
                "foto_fat": checkin.foto_fat,
                "latitude_fat": float(checkin.latitude_fat),
                "longitude_fat": float(checkin.longitude_fat),
                "jarak_meter": float(checkin.jarak_meter),
                "status_pemesanan": checkin.status_pemesanan,
                "tanggal_po": checkin.tanggal_po,
                "tanggal_so": checkin.tanggal_so,
                "tanggal_sa": checkin.tanggal_sa,
                "tanggal_pa": checkin.tanggal_pa,
                "kode_sales": checkin.kode_sales,
                "status_checkin": checkin.status_checkin,
                "tanggal_form1": checkin.tanggal_form1,
                "tanggal_checkin": checkin.tanggal_checkin,
            },
            "fat_baru": {
                "id": fat_baru.id,
                "fat_code": fat_baru.fat_code,
                "tipe_fat": fat_baru.tipe_fat,
                "longitude_fat": float(fat_baru.longitude_fat),
                "latitude_fat": float(fat_baru.latitude_fat),
                "slot_available": fat_baru.slot_available,
                "slot_occupied": fat_baru.slot_occupied,
                "date": fat_baru.date,
                "status": fat_baru.status,
                "sales_code": fat_baru.sales_code,
            }
        }

    @staticmethod
    def join_new_fat(
        db3: Session,
        no_activity: str,
        kode_sales: str,
        fat_code: str,
        status_pemesanan: str
    ) -> dict:
        checkin, fat_baru = crud.merge_and_join_new_fat(
            db3=db3,
            no_activity=no_activity,
            kode_sales=kode_sales,
            fat_code=fat_code,
            status_pemesanan=status_pemesanan
        )
        
        return {
            "checkin": {
                "id": checkin.id,
                "no_activity": checkin.no_activity,
                "nama_pelanggan": checkin.nama_pelanggan,
                "nomor_pelanggan": checkin.nomor_pelanggan,
                "latitude_rumah": float(checkin.latitude_rumah),
                "longitude_rumah": float(checkin.longitude_rumah),
                "keterangan": checkin.keterangan,
                "fat_code": checkin.fat_code,
                "foto_fat": checkin.foto_fat,
                "latitude_fat": float(checkin.latitude_fat),
                "longitude_fat": float(checkin.longitude_fat),
                "jarak_meter": float(checkin.jarak_meter),
                "status_pemesanan": checkin.status_pemesanan,
                "tanggal_po": checkin.tanggal_po,
                "tanggal_so": checkin.tanggal_so,
                "tanggal_sa": checkin.tanggal_sa,
                "tanggal_pa": checkin.tanggal_pa,
                "kode_sales": checkin.kode_sales,
                "status_checkin": checkin.status_checkin,
                "tanggal_form1": checkin.tanggal_form1,
                "tanggal_checkin": checkin.tanggal_checkin,
            },
            "fat_baru": {
                "id": fat_baru.id,
                "fat_code": fat_baru.fat_code,
                "tipe_fat": fat_baru.tipe_fat,
                "longitude_fat": float(fat_baru.longitude_fat),
                "latitude_fat": float(fat_baru.latitude_fat),
                "slot_available": fat_baru.slot_available,
                "slot_occupied": fat_baru.slot_occupied,
                "date": fat_baru.date,
                "status": fat_baru.status,
                "sales_code": fat_baru.sales_code,
            }
        }

    @staticmethod
    def get_nearby_fat_baru(
        db3: Session,
        lat_user: float,
        lon_user: float,
        max_radius_km: float = 5.0
    ) -> list[dict]:
        return crud.get_nearby_fat_baru(db3, lat_user, lon_user, max_radius_km)

    @staticmethod
    def get_fat_baru_detail(db3: Session, fat_code: str) -> dict:
        fat = crud.get_fat_baru_detail(db3, fat_code)
        if not fat:
            raise ValueError(f"Data FAT Baru dengan kode {fat_code} tidak ditemukan.")
            
        checkins = crud.get_checkins_by_fat_code(db3, fat_code)
        checkins_data = [
            {
                "no_activity": c.no_activity,
                "nama_pelanggan": c.nama_pelanggan,
                "nomor_pelanggan": c.nomor_pelanggan
            } for c in checkins
        ]
            
        return {
            "id": fat.id,
            "fat_code": fat.fat_code,
            "tipe_fat": fat.tipe_fat,
            "longitude_fat": float(fat.longitude_fat),
            "latitude_fat": float(fat.latitude_fat),
            "slot_available": fat.slot_available,
            "slot_occupied": fat.slot_occupied,
            "date": fat.date,
            "status": fat.status,
            "sales_code": fat.sales_code,
            "checkins": checkins_data,
        }

    @staticmethod
    def get_done_detail(db3: Session, no_activity: str, kode_sales: str) -> dict:
        done = crud.get_done_by_no_activity(db3, no_activity, kode_sales)
        if not done:
            raise ValueError(f"Data checkin dengan no_activity {no_activity} tidak ditemukan.")
            
        return {
            "id": done.id,
            "no_activity": done.no_activity,
            "nama_pelanggan": done.nama_pelanggan,
            "nomor_pelanggan": done.nomor_pelanggan,
            "latitude_rumah": float(done.latitude_rumah),
            "longitude_rumah": float(done.longitude_rumah),
            "keterangan": done.keterangan,
            "fat_code": done.fat_code,
            "foto_fat": done.foto_fat,
            "latitude_fat": float(done.latitude_fat),
            "longitude_fat": float(done.longitude_fat),
            "jarak_meter": float(done.jarak_meter),
            "status_pemesanan": done.status_pemesanan,
            "tanggal_po": done.tanggal_po,
            "tanggal_so": done.tanggal_so,
            "tanggal_sa": done.tanggal_sa,
            "tanggal_pa": done.tanggal_pa,
            "kode_sales": done.kode_sales,
            "status_checkin": done.status_checkin,
            "tanggal_form1": done.tanggal_form1,
            "tanggal_checkin": done.tanggal_checkin,
        }

    @staticmethod
    def get_pemesanan_status(
        db3: Session,
        kode_sales: str,
        start_date: str = None,
        end_date: str = None,
        status_filter: str = None
    ) -> list[dict]:
        records = crud.get_pemesanan_status_list(db3, kode_sales, start_date, end_date, status_filter)
        return [
            {
                "id": r.id,
                "no_activity": r.no_activity,
                "nama_pelanggan": r.nama_pelanggan,
                "tanggal_checkin": r.tanggal_checkin,
                "status_pemesanan": r.status_pemesanan,
                "kode_sales": r.kode_sales,
            }
            for r in records
        ]

    @staticmethod
    def update_checkin_status(
        db3: Session,
        no_activity: str,
        kode_sales: str,
        status_pemesanan: str,
        keterangan: str | None = None
    ) -> dict:
        checkin = crud.update_checkin_status(db3, no_activity, kode_sales, status_pemesanan, keterangan)
        return {
            "id": checkin.id,
            "no_activity": checkin.no_activity,
            "nama_pelanggan": checkin.nama_pelanggan,
            "nomor_pelanggan": checkin.nomor_pelanggan,
            "latitude_rumah": float(checkin.latitude_rumah),
            "longitude_rumah": float(checkin.longitude_rumah),
            "keterangan": checkin.keterangan,
            "fat_code": checkin.fat_code,
            "foto_fat": checkin.foto_fat,
            "latitude_fat": float(checkin.latitude_fat),
            "longitude_fat": float(checkin.longitude_fat),
            "jarak_meter": float(checkin.jarak_meter),
            "status_pemesanan": checkin.status_pemesanan,
            "tanggal_po": checkin.tanggal_po,
            "tanggal_so": checkin.tanggal_so,
            "tanggal_sa": checkin.tanggal_sa,
            "tanggal_pa": checkin.tanggal_pa,
            "kode_sales": checkin.kode_sales,
            "status_checkin": checkin.status_checkin,
            "tanggal_form1": checkin.tanggal_form1,
            "tanggal_checkin": checkin.tanggal_checkin,
        }

    @staticmethod
    async def verify_fat_logic(db2: Session, image_data: bytes, lat_sales: float, lon_sales: float) -> dict:
        from app.utils.inference import detect_and_ocr_fat
        from sqlalchemy import func
        
        # 1. Jalankan Deteksi YOLO & OCR
        detection_res = await detect_and_ocr_fat(image_data)
        
        # Jika kotak fisik FAT tidak terdeteksi oleh YOLO
        if not detection_res["box_detected"]:
            return {
                "verified": False,
                "verification_method": "FAILED",
                "detected_fat_code": None,
                "raw_ocr_text": None,
                "ocr_time_ms": None,
                "distance_meters": None,
                "message": "Gagal Verifikasi: Kotak fisik FAT tidak terdeteksi pada gambar."
            }
        
        ocr_raw_text = detection_res["ocr_text"]
        ocr_time_ms = detection_res.get("ocr_time_ms")
        
        # 2. Coba cari kecocokan kode FAT dari database jika teks OCR tidak kosong
        matched_fat = None
        if ocr_raw_text:
            # Pindahkan pencarian ke sisi Database agar lebih cepat (tidak menarik seluruh tabel ke RAM)
            matched_fat = db2.query(FTTH_DETAIL_FAT_NEAR_ME).filter(
                FTTH_DETAIL_FAT_NEAR_ME.KODE.isnot(None),
                func.trim(FTTH_DETAIL_FAT_NEAR_ME.KODE) != '',
                func.upper(ocr_raw_text).like(func.concat('%', func.trim(func.upper(FTTH_DETAIL_FAT_NEAR_ME.KODE)), '%'))
            ).first()
                    
        # 3. JALUR UTAMA (OCR berhasil mencocokkan kode di database)
        if matched_fat:
            lat_fat = float(matched_fat.LATITUDE)
            lon_fat = float(matched_fat.LONGITUDE)
            
            # Hitung jarak sales dengan lokasi FAT terdaftar
            distance = crud.validate_radius(lat_sales, lon_sales, lat_fat, lon_fat)
            
            # Cek radius toleransi (25 meter)
            if distance <= 25.0:
                return {
                    "verified": True,
                    "verification_method": "OCR",
                    "detected_fat_code": matched_fat.KODE,
                    "raw_ocr_text": ocr_raw_text,
                    "ocr_time_ms": ocr_time_ms,
                    "distance_meters": round(distance, 2),
                    "message": f"Verifikasi Sukses via OCR. Kotak terdeteksi dan kode {matched_fat.KODE} cocok."
                }
                
        # 4. JALUR FALLBACK (OCR gagal / tidak cocok, tetapi kotak fisik terdeteksi oleh YOLO)
        # Cari kotak FAT terdekat dari lokasi koordinat sales
        # Menggunakan Bounding Box 50 meter (+/- 0.0005 derajat) di sisi Database agar sangat cepat
        min_lat = lat_sales - 0.0005
        max_lat = lat_sales + 0.0005
        min_lon = lon_sales - 0.0005
        max_lon = lon_sales + 0.0005
        
        from sqlalchemy import text
        sql = text("""
            WITH valid_fats AS (
                SELECT "KODE", CAST("LATITUDE" AS FLOAT) AS lat, CAST("LONGITUDE" AS FLOAT) AS lon
                FROM "FTTH_DETAIL_FAT_NEAR_ME"
                WHERE "LATITUDE" ~ '^[-+]?[0-9]*\.?[0-9]+$'
                  AND "LONGITUDE" ~ '^[-+]?[0-9]*\.?[0-9]+$'
            )
            SELECT "KODE", lat, lon
            FROM valid_fats
            WHERE lat BETWEEN :min_lat AND :max_lat
              AND lon BETWEEN :min_lon AND :max_lon
        """)
        
        all_fats = db2.execute(sql, {
            "min_lat": min_lat, "max_lat": max_lat, 
            "min_lon": min_lon, "max_lon": max_lon
        }).fetchall()
        
        nearest_fat_kode = None
        min_distance = float('inf')
        
        for fat in all_fats:
            lat_fat = fat[1]
            lon_fat = fat[2]
            dist = crud.validate_radius(lat_sales, lon_sales, lat_fat, lon_fat)
            if dist < min_distance:
                min_distance = dist
                nearest_fat_kode = fat[0]
                
        # Jika tiang FAT terdekat masih dalam radius toleransi
        if nearest_fat_kode and min_distance <= 25.0:
            return {
                "verified": True,
                "verification_method": "PROXIMITY_FALLBACK",
                "detected_fat_code": nearest_fat_kode,
                "raw_ocr_text": ocr_raw_text,
                "ocr_time_ms": ocr_time_ms,
                "distance_meters": round(min_distance, 2),
                "message": f"Verifikasi Sukses via Proximity (Fallback). Teks tidak terbaca jelas, namun lokasi Anda berada di dekat tiang FAT {nearest_fat_kode}."
            }
            
        # Jika kedua jalur verifikasi di atas gagal
        return {
            "verified": False,
            "verification_method": "FAILED",
            "detected_fat_code": nearest_fat_kode if nearest_fat_kode else None,
            "raw_ocr_text": ocr_raw_text,
            "ocr_time_ms": ocr_time_ms,
            "distance_meters": round(min_distance, 2) if nearest_fat_kode else None,
            "message": "Gagal Verifikasi: Lokasi koordinat Anda terlalu jauh dari tiang FAT terdekat."
        }

    @staticmethod
    async def verify_fat_performance_logic(db2: Session, image_data: bytes, lat_sales: float, lon_sales: float) -> dict:
        from app.utils.inference import detect_and_ocr_fat
        from sqlalchemy import func
        import time
        
        # 1. Jalankan Deteksi YOLO & OCR
        detection_res = await detect_and_ocr_fat(image_data)
        
        # Jika kotak fisik FAT tidak terdeteksi oleh YOLO
        if not detection_res["box_detected"]:
            return {
                "verified": False,
                "verification_method": "FAILED",
                "detected_fat_code": None,
                "raw_ocr_text": None,
                "ocr_time_ms": None,
                "yolo_time_ms": detection_res.get("yolo_time_ms"),
                "spatial_validation_time_ms": None,
                "distance_meters": None,
                "message": "Gagal Verifikasi: Kotak fisik FAT tidak terdeteksi pada gambar."
            }
        
        ocr_raw_text = detection_res["ocr_text"]
        ocr_time_ms = detection_res.get("ocr_time_ms")
        yolo_time_ms = detection_res.get("yolo_time_ms")
        
        # Waktu kalkulasi spasial dimulai
        start_spatial = time.perf_counter()
        
        # 2. Coba cari kecocokan kode FAT dari database
        matched_fat = None
        if ocr_raw_text:
            matched_fat = db2.query(FTTH_DETAIL_FAT_NEAR_ME).filter(
                FTTH_DETAIL_FAT_NEAR_ME.KODE.isnot(None),
                func.trim(FTTH_DETAIL_FAT_NEAR_ME.KODE) != '',
                func.upper(ocr_raw_text).like(func.concat('%', func.trim(func.upper(FTTH_DETAIL_FAT_NEAR_ME.KODE)), '%'))
            ).first()
                    
        # 3. JALUR UTAMA
        if matched_fat:
            lat_fat = float(matched_fat.LATITUDE)
            lon_fat = float(matched_fat.LONGITUDE)
            distance = crud.validate_radius(lat_sales, lon_sales, lat_fat, lon_fat)
            
            if distance <= 25.0:
                spatial_time_ms = (time.perf_counter() - start_spatial) * 1000
                return {
                    "verified": True,
                    "verification_method": "OCR",
                    "detected_fat_code": matched_fat.KODE,
                    "raw_ocr_text": ocr_raw_text,
                    "ocr_time_ms": ocr_time_ms,
                    "yolo_time_ms": yolo_time_ms,
                    "spatial_validation_time_ms": round(spatial_time_ms, 2),
                    "distance_meters": round(distance, 2),
                    "message": f"Verifikasi Sukses via OCR. Kotak terdeteksi dan kode {matched_fat.KODE} cocok."
                }
                
        # 4. JALUR FALLBACK
        min_lat = lat_sales - 0.0005
        max_lat = lat_sales + 0.0005
        min_lon = lon_sales - 0.0005
        max_lon = lon_sales + 0.0005
        
        from sqlalchemy import text
        sql = text("""
            WITH valid_fats AS (
                SELECT "KODE", CAST("LATITUDE" AS FLOAT) AS lat, CAST("LONGITUDE" AS FLOAT) AS lon
                FROM "FTTH_DETAIL_FAT_NEAR_ME"
                WHERE "LATITUDE" ~ '^[-+]?[0-9]*\.?[0-9]+$'
                  AND "LONGITUDE" ~ '^[-+]?[0-9]*\.?[0-9]+$'
            )
            SELECT "KODE", lat, lon
            FROM valid_fats
            WHERE lat BETWEEN :min_lat AND :max_lat
              AND lon BETWEEN :min_lon AND :max_lon
        """)
        
        all_fats = db2.execute(sql, {
            "min_lat": min_lat, "max_lat": max_lat, 
            "min_lon": min_lon, "max_lon": max_lon
        }).fetchall()
        
        nearest_fat_kode = None
        min_distance = float('inf')
        
        for fat in all_fats:
            lat_fat = fat[1]
            lon_fat = fat[2]
            dist = crud.validate_radius(lat_sales, lon_sales, lat_fat, lon_fat)
            if dist < min_distance:
                min_distance = dist
                nearest_fat_kode = fat[0]
                
        spatial_time_ms = (time.perf_counter() - start_spatial) * 1000
                
        if nearest_fat_kode and min_distance <= 25.0:
            return {
                "verified": True,
                "verification_method": "PROXIMITY_FALLBACK",
                "detected_fat_code": nearest_fat_kode,
                "raw_ocr_text": ocr_raw_text,
                "ocr_time_ms": ocr_time_ms,
                "yolo_time_ms": yolo_time_ms,
                "spatial_validation_time_ms": round(spatial_time_ms, 2),
                "distance_meters": round(min_distance, 2),
                "message": f"Verifikasi Sukses via Proximity (Fallback). Lokasi dekat tiang FAT {nearest_fat_kode}."
            }
            
        return {
            "verified": False,
            "verification_method": "FAILED",
            "detected_fat_code": nearest_fat_kode if nearest_fat_kode else None,
            "raw_ocr_text": ocr_raw_text,
            "ocr_time_ms": ocr_time_ms,
            "yolo_time_ms": yolo_time_ms,
            "spatial_validation_time_ms": round(spatial_time_ms, 2),
            "distance_meters": round(min_distance, 2) if nearest_fat_kode else None,
            "message": "Gagal Verifikasi: Lokasi koordinat Anda terlalu jauh dari tiang FAT terdekat."
        }

    @staticmethod
    def update_fat_new(db3: Session, no_activity: str, kode_sales: str, payload_data: dict) -> dict:
        tipe_fat = payload_data.get("tipe_fat", "")
        if "16" in tipe_fat:
            slot_available = 15
            slot_occupied = 1
        elif "24" in tipe_fat:
            slot_available = 23
            slot_occupied = 1
        else:
            slot_available = 7
            slot_occupied = 1
            
        fat_code = crud.generate_fat_code(db3)
        
        new_fat_data = {
            "fat_code": fat_code,
            "tipe_fat": tipe_fat,
            "longitude_fat": payload_data["longitude_fat"],
            "latitude_fat": payload_data["latitude_fat"],
            "slot_available": slot_available,
            "slot_occupied": slot_occupied
        }
        
        checkin, fat_baru = crud.update_checkin_new_fat(db3, no_activity, kode_sales, new_fat_data)
        
        return {
            "checkin": {
                "id": checkin.id,
                "no_activity": checkin.no_activity,
                "nama_pelanggan": checkin.nama_pelanggan,
                "nomor_pelanggan": checkin.nomor_pelanggan,
                "latitude_rumah": float(checkin.latitude_rumah),
                "longitude_rumah": float(checkin.longitude_rumah),
                "keterangan": checkin.keterangan,
                "fat_code": checkin.fat_code,
                "foto_fat": checkin.foto_fat,
                "latitude_fat": float(checkin.latitude_fat),
                "longitude_fat": float(checkin.longitude_fat),
                "jarak_meter": float(checkin.jarak_meter),
                "status_pemesanan": checkin.status_pemesanan,
                "tanggal_po": checkin.tanggal_po,
                "tanggal_so": checkin.tanggal_so,
                "tanggal_sa": checkin.tanggal_sa,
                "tanggal_pa": checkin.tanggal_pa,
                "kode_sales": checkin.kode_sales,
                "status_checkin": checkin.status_checkin,
                "tanggal_form1": checkin.tanggal_form1,
                "tanggal_checkin": checkin.tanggal_checkin,
            },
            "fat_baru": {
                "id": fat_baru.id,
                "fat_code": fat_baru.fat_code,
                "tipe_fat": fat_baru.tipe_fat,
                "longitude_fat": float(fat_baru.longitude_fat),
                "latitude_fat": float(fat_baru.latitude_fat),
                "slot_available": fat_baru.slot_available,
                "slot_occupied": fat_baru.slot_occupied,
                "date": fat_baru.date,
                "status": fat_baru.status,
                "sales_code": fat_baru.sales_code,
            }
        }

    @staticmethod
    def update_fat_join(db3: Session, no_activity: str, kode_sales: str, fat_code: str) -> dict:
        checkin, fat_baru = crud.update_checkin_join_fat(db3, no_activity, kode_sales, fat_code)
        
        return {
            "checkin": {
                "id": checkin.id,
                "no_activity": checkin.no_activity,
                "nama_pelanggan": checkin.nama_pelanggan,
                "nomor_pelanggan": checkin.nomor_pelanggan,
                "latitude_rumah": float(checkin.latitude_rumah),
                "longitude_rumah": float(checkin.longitude_rumah),
                "keterangan": checkin.keterangan,
                "fat_code": checkin.fat_code,
                "foto_fat": checkin.foto_fat,
                "latitude_fat": float(checkin.latitude_fat),
                "longitude_fat": float(checkin.longitude_fat),
                "jarak_meter": float(checkin.jarak_meter),
                "status_pemesanan": checkin.status_pemesanan,
                "tanggal_po": checkin.tanggal_po,
                "tanggal_so": checkin.tanggal_so,
                "tanggal_sa": checkin.tanggal_sa,
                "tanggal_pa": checkin.tanggal_pa,
                "kode_sales": checkin.kode_sales,
                "status_checkin": checkin.status_checkin,
                "tanggal_form1": checkin.tanggal_form1,
                "tanggal_checkin": checkin.tanggal_checkin,
            },
            "fat_baru": {
                "id": fat_baru.id,
                "fat_code": fat_baru.fat_code,
                "tipe_fat": fat_baru.tipe_fat,
                "longitude_fat": float(fat_baru.longitude_fat),
                "latitude_fat": float(fat_baru.latitude_fat),
                "slot_available": fat_baru.slot_available,
                "slot_occupied": fat_baru.slot_occupied,
                "date": fat_baru.date,
                "status": fat_baru.status,
                "sales_code": fat_baru.sales_code,
            }
        }
