"""
FastAPI Backend untuk Aplikasi Absen Karyawan Berbasis Online & Integrasi HRIS Pengupahan - Bagian HRD.
"""

import os
import socket
from datetime import date, datetime
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import HTMLResponse, JSONResponse, Response
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates

from app.database import init_db, SHIFT_DEFINITIONS, get_schedule_for_shift
from app.crud import (
    list_locations,
    save_location,
    delete_location,
    list_employees,
    save_employee,
    delete_employee,
    submit_attendance,
    delete_attendance_log,
    list_attendance_logs,
    get_recapitulation_and_hris,
    get_employee_by_token,
    get_attendance_log,
    get_recent_attendance,
    get_location_by_id,
    submit_attendance_by_token,
    get_rekap_periode,
    save_employee_face,
    get_employee_face,
)
from app.export_service import generate_hris_excel

APP_DIR = os.path.dirname(os.path.abspath(__file__))
STATIC_DIR = os.path.join(APP_DIR, "static")
TEMPLATES_DIR = os.path.join(APP_DIR, "templates")

os.makedirs(STATIC_DIR, exist_ok=True)
os.makedirs(TEMPLATES_DIR, exist_ok=True)

app = FastAPI(
    title="Aplikasi Absen Karyawan Online & Integrasi HRIS - Bagian HRD",
    description="Sistem Absensi GPS HP Karyawan (Office & Proyek) Terintegrasi HRIS Pengupahan",
    version="2.0.0",
)

app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
templates = Jinja2Templates(directory=TEMPLATES_DIR)


@app.on_event("startup")
def on_startup():
    init_db()


def _get_local_ip() -> str:
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(("8.8.8.8", 80))
        ip = s.getsockname()[0]
        s.close()
        return ip
    except Exception:
        return "127.0.0.1"


def get_public_base_url(request: Request) -> str:
    """
    Alamat dasar untuk link absen yang dibagikan ke HP karyawan.
    - Jika variabel lingkungan PUBLIC_BASE_URL diisi (misal https://absen.perusahaan.com), itu yang dipakai.
    - Jika admin membuka lewat localhost/127.0.0.1, diganti IP WiFi/LAN komputer ini
      agar link bisa dibuka dari HP yang satu jaringan.
    """
    env_url = os.environ.get("PUBLIC_BASE_URL", "").strip().rstrip("/")
    if env_url:
        return env_url
    host = request.url.hostname or "127.0.0.1"
    if host in ("127.0.0.1", "localhost", "0.0.0.0", "::1"):
        host = _get_local_ip()
    port = request.url.port
    scheme = request.url.scheme
    default_port = (scheme == "https" and port in (None, 443)) or (scheme == "http" and port in (None, 80))
    return f"{scheme}://{host}" + ("" if default_port else f":{port}")


@app.get("/", response_class=HTMLResponse)
async def index(request: Request):
    init_db()
    today_str = date.today().strftime("%Y-%m-%d")
    # Versi aset (waktu ubah file) agar browser selalu memuat CSS/JS terbaru, bukan cache lama
    asset_v = 0
    for rel in ("css/style.css", "js/app.js"):
        try:
            asset_v = max(asset_v, int(os.path.getmtime(os.path.join(STATIC_DIR, rel))))
        except OSError:
            pass
    return templates.TemplateResponse(
        request,
        "index.html",
        {
            "today_str": today_str,
            "asset_v": asset_v,
        },
    )


@app.get("/preview-absen", response_class=HTMLResponse)
async def preview_absen_page(request: Request):
    """Halaman preview langsung tampilan HP (Screenshot 1 + Screenshot 2 Leaflet GPS)"""
    return templates.TemplateResponse(request, "preview_absen_hp.html", {})


@app.get("/api/karyawan/{emp_id}/profile")
async def api_karyawan_profile(emp_id: int):
    """Mengambil data profil karyawan beserta foto wajah acuan master."""
    emp = get_employee_face(emp_id)
    if not emp:
        return JSONResponse({"status": "error", "message": "Karyawan tidak ditemukan"}, status_code=404)
    return JSONResponse({"status": "ok", "data": emp})


@app.post("/api/karyawan/{emp_id}/register-face")
async def api_karyawan_register_face(emp_id: int, request: Request):
    """Mendaftarkan atau memperbarui foto wajah master karyawan sebagai acuan permanen."""
    try:
        payload = await request.json()
        face_base64 = payload.get("face_template") or payload.get("foto_wajah")
        if not face_base64:
            raise HTTPException(status_code=400, detail="Data foto wajah tidak valid.")
        save_employee_face(emp_id, face_base64)
        return JSONResponse({
            "status": "ok",
            "message": "Foto wajah berhasil didaftarkan sebagai acuan resmi karyawan!",
            "emp_id": emp_id
        })
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))



@app.get("/absen/{token}", response_class=HTMLResponse)
async def absen_karyawan_page(request: Request, token: str):
    """Halaman absen pribadi karyawan (dibuka dari HP lewat link unik)."""
    emp = get_employee_by_token(token)
    if not emp:
        return templates.TemplateResponse(
            request, "preview_absen_hp.html", {"token": token, "valid": False}, status_code=404
        )
    return templates.TemplateResponse(request, "preview_absen_hp.html", {"token": token, "valid": True})


@app.get("/api/absen/{token}/info")
async def api_absen_info(token: str):
    emp = get_employee_by_token(token)
    if not emp:
        raise HTTPException(status_code=404, detail="Link absen tidak valid.")

    now = datetime.now()
    tanggal = now.strftime("%Y-%m-%d")
    loc = get_location_by_id(emp["lokasi_default_id"]) if emp.get("lokasi_default_id") else None
    sched = get_schedule_for_shift(emp["shift_default"], tanggal, emp["kategori_kerja"])
    today_log = get_attendance_log(emp["id"], tanggal)

    return JSONResponse(
        {
            "karyawan": {
                "nama": emp["nama"],
                "nip": emp["nip"],
                "perusahaan": emp["perusahaan"],
                "jabatan": emp["jabatan"],
                "kategori_kerja": emp["kategori_kerja"],
                "nama_lokasi": emp["nama_lokasi_default"],
            },
            "lokasi": (
                {
                    "latitude": loc["latitude"],
                    "longitude": loc["longitude"],
                    "radius_meter": loc["radius_meter"],
                    "mode_radius": loc["mode_radius"],
                }
                if loc
                else None
            ),
            "jadwal": sched,
            "server_date": tanggal,
            "server_time": now.strftime("%H:%M:%S"),
            "log_hari_ini": today_log,
            "riwayat": get_recent_attendance(emp["id"], 7),
        }
    )


@app.post("/api/absen/{token}/submit")
async def api_absen_submit(token: str, request: Request):
    try:
        payload = await request.json()
        saved = submit_attendance_by_token(
            token,
            payload.get("tipe_aksi"),
            payload.get("latitude"),
            payload.get("longitude"),
            payload.get("catatan", ""),
        )
        return JSONResponse({"status": "ok", "message": "Absensi berhasil disimpan.", "data": saved})
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Terjadi kesalahan server: {e}")


@app.get("/api/bootstrap")
async def api_bootstrap(
    request: Request,
    start_date: str = "",
    end_date: str = "",
    perusahaan: str = "ALL",
    departemen: str = "ALL",
    kategori_kerja: str = "ALL",
    lokasi_id: str = "ALL",
    status_kehadiran: str = "ALL",
    search: str = "",
):
    today_str = date.today().strftime("%Y-%m-%d")
    if not start_date:
        start_date = f"{today_str[:7]}-01"
    if not end_date:
        end_date = today_str

    locations = list_locations()
    employees = list_employees(
        search=search,
        perusahaan=perusahaan,
        departemen=departemen,
        kategori_kerja=kategori_kerja,
    )
    all_employees = list_employees()
    logs = list_attendance_logs(
        start_date=start_date,
        end_date=end_date,
        kategori_kerja=kategori_kerja,
        lokasi_id=lokasi_id,
        status_kehadiran=status_kehadiran,
        search=search,
    )
    rekap = get_recapitulation_and_hris(
        start_date=start_date,
        end_date=end_date,
        perusahaan=perusahaan,
        departemen=departemen,
        kategori_kerja=kategori_kerja,
        search=search,
    )

    perusahaan_list = sorted(list({e["perusahaan"] for e in all_employees if e.get("perusahaan")}))
    departemen_list = sorted(list({e["departemen"] for e in all_employees if e.get("departemen")}))

    return JSONResponse(
        {
            "today": today_str,
            "base_url": get_public_base_url(request),
            "start_date": start_date,
            "end_date": end_date,
            "shifts": SHIFT_DEFINITIONS,
            "locations": locations,
            "employees": employees,
            "all_employees": all_employees,
            "perusahaan_list": perusahaan_list,
            "departemen_list": departemen_list,
            "logs": logs,
            "rekap": rekap,
        }
    )


@app.get("/api/rekap-periode")
async def api_rekap_periode(
    start_date: str = "",
    end_date: str = "",
    perusahaan: str = "ALL",
    departemen: str = "ALL",
    employee_id: str = "ALL",
    status_kehadiran: str = "ALL",
    all_time: bool = False,
):
    return JSONResponse(
        get_rekap_periode(
            start_date=start_date,
            end_date=end_date,
            perusahaan=perusahaan,
            departemen=departemen,
            employee_id=employee_id,
            status_kehadiran=status_kehadiran,
            all_time=all_time,
        )
    )


@app.get("/api/schedule-preview")
async def api_schedule_preview(shift_kode: str = "OFFICE", tanggal: str = "", kategori_kerja: str = "Office"):
    if not tanggal:
        tanggal = date.today().strftime("%Y-%m-%d")
    return JSONResponse(get_schedule_for_shift(shift_kode, tanggal, kategori_kerja))


@app.post("/api/attendance")
async def api_submit_attendance(request: Request):
    try:
        payload = await request.json()
        saved = submit_attendance(payload)
        return JSONResponse({"status": "ok", "message": "Absensi berhasil disimpan.", "data": saved})
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.delete("/api/attendance/{log_id}")
async def api_delete_attendance(log_id: int):
    delete_attendance_log(log_id)
    return JSONResponse({"status": "ok", "message": "Data log absensi dihapus."})


@app.post("/api/employees")
async def api_save_employee(request: Request):
    try:
        payload = await request.json()
        emp_id = save_employee(payload)
        return JSONResponse({"status": "ok", "message": "Data karyawan berhasil disimpan.", "id": emp_id})
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.delete("/api/employees/{emp_id}")
async def api_delete_employee(emp_id: int):
    delete_employee(emp_id)
    return JSONResponse({"status": "ok", "message": "Data karyawan berhasil dihapus."})


@app.post("/api/locations")
async def api_save_location(request: Request):
    try:
        payload = await request.json()
        loc_id = save_location(payload)
        return JSONResponse({"status": "ok", "message": "Lokasi kerja berhasil disimpan.", "id": loc_id})
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.delete("/api/locations/{loc_id}")
async def api_delete_location(loc_id: int):
    delete_location(loc_id)
    return JSONResponse({"status": "ok", "message": "Lokasi kerja berhasil dihapus."})


@app.get("/api/export-hris-excel")
async def api_export_hris_excel(
    start_date: str = "",
    end_date: str = "",
    perusahaan: str = "ALL",
    departemen: str = "ALL",
    kategori_kerja: str = "ALL",
    search: str = "",
):
    today_str = date.today().strftime("%Y-%m-%d")
    if not start_date:
        start_date = f"{today_str[:7]}-01"
    if not end_date:
        end_date = today_str

    rekap = get_recapitulation_and_hris(
        start_date=start_date,
        end_date=end_date,
        perusahaan=perusahaan,
        departemen=departemen,
        kategori_kerja=kategori_kerja,
        search=search,
    )
    logs = list_attendance_logs(
        start_date=start_date,
        end_date=end_date,
        kategori_kerja=kategori_kerja,
        search=search,
    )
    locations = list_locations()
    xlsx_bytes = generate_hris_excel(rekap, logs, locations, start_date, end_date)
    filename = f"Rekap_Absen_HRIS_{start_date}_sd_{end_date}.xlsx"
    return Response(
        content=xlsx_bytes,
        media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        headers={"Content-Disposition": f'attachment; filename="{filename}"'},
    )
