<?php

namespace App\Filament\Imports;

use App\Models\Attendance;
use App\Models\User;
use Filament\Actions\Imports\ImportColumn;
use Filament\Actions\Imports\Importer;
use Filament\Actions\Imports\Models\Import;
use Carbon\Carbon;

class AttendanceImporter extends Importer
{
    protected static ?string $model = Attendance::class;

    public static function getColumns(): array
    {
        // Kolom persis sama dengan AttendanceExporter
        return [
            ImportColumn::make('id')
                ->label('ID')
                ->rules(['nullable', 'string']),  // UUID string

            ImportColumn::make('date')
                ->label('Tanggal')
                ->requiredMapping()
                ->rules(['required'])
                ->example('2026-01-03'),

            ImportColumn::make('user_name')
                ->label('Nama Karyawan')
                ->requiredMapping()
                ->rules(['required', 'string'])
                ->example('John Doe'),

            ImportColumn::make('employee_id')
                ->label('NIP')
                ->rules(['nullable', 'string'])
                ->example('12345'),

            ImportColumn::make('company_name')
                ->label('Perusahaan')
                ->rules(['nullable', 'string'])
                ->example('PT Example'),

            ImportColumn::make('office_name')
                ->label('Kantor')
                ->rules(['nullable', 'string'])
                ->example('Kantor Pusat'),

            ImportColumn::make('type')
                ->label('Tipe')
                ->rules(['nullable', 'string'])
                ->example('Kantor'),

            ImportColumn::make('check_in_time')
                ->label('Jam Masuk')
                ->requiredMapping()
                ->rules(['required'])
                ->example('08:00:00'),

            ImportColumn::make('check_in_latitude')
                ->label('Lat Masuk')
                ->rules(['nullable'])
                ->example('-6.175392'),

            ImportColumn::make('check_in_longitude')
                ->label('Long Masuk')
                ->rules(['nullable'])
                ->example('106.827153'),

            ImportColumn::make('check_in_address')
                ->label('Alamat Masuk')
                ->rules(['nullable', 'string'])
                ->example('Jl. Example No. 1'),

            ImportColumn::make('check_out_time')
                ->label('Jam Pulang')
                ->rules(['nullable'])
                ->example('17:00:00'),

            ImportColumn::make('check_out_latitude')
                ->label('Lat Pulang')
                ->rules(['nullable'])
                ->example('-6.175392'),

            ImportColumn::make('check_out_longitude')
                ->label('Long Pulang')
                ->rules(['nullable'])
                ->example('106.827153'),

            ImportColumn::make('check_out_address')
                ->label('Alamat Pulang')
                ->rules(['nullable', 'string'])
                ->example('Jl. Example No. 1'),

            ImportColumn::make('work_duration')
                ->label('Durasi Kerja (Menit)')
                ->rules(['nullable', 'integer'])
                ->example('480'),

            ImportColumn::make('overtime_duration')
                ->label('Lembur (Menit)')
                ->rules(['nullable', 'integer'])
                ->example('60'),

            ImportColumn::make('status')
                ->label('Status')
                ->rules(['nullable', 'string'])
                ->example('Hadir'),

            ImportColumn::make('check_in_notes')
                ->label('Catatan Masuk')
                ->rules(['nullable', 'string'])
                ->example('On time'),

            ImportColumn::make('check_out_notes')
                ->label('Catatan Pulang')
                ->rules(['nullable', 'string'])
                ->example('Normal checkout'),

            ImportColumn::make('created_at')
                ->label('Dibuat')
                ->rules(['nullable']),
        ];
    }

    public function resolveRecord(): ?Attendance
    {
        $data = $this->data;

        // Find user by employee_id (exact match) or name (exact match)
        $user = null;

        // Priority 1: Employee ID (exact match)
        if (!empty($data['employee_id'])) {
            $user = User::where('employee_id', trim($data['employee_id']))->first();
        }

        // Priority 2: Name - EXACT MATCH (bukan LIKE untuk menghindari false positives)
        if (!$user && !empty($data['user_name'])) {
            $inputName = trim($data['user_name']);

            // Try exact match (case-insensitive)
            $user = User::whereRaw('LOWER(name) = ?', [strtolower($inputName)])->first();

            // If not found, try normalized match (remove extra spaces and dots)
            if (!$user) {
                $normalizedInput = $this->normalizeName($inputName);
                $user = User::get()->first(function ($u) use ($normalizedInput) {
                    return $this->normalizeName($u->name) === $normalizedInput;
                });
            }
        }

        if (!$user) {
            return null; // Skip if user not found
        }

        // Skip if user is super_admin
        if ($user->hasRole('super_admin')) {
            return null;
        }

        // Parse date
        $date = $this->parseDate($data['date'] ?? null);
        if (!$date) {
            return null;
        }

        // Parse times
        $checkInTime = $this->parseTime($data['check_in_time'] ?? null, $date);
        $checkOutTime = $this->parseTime($data['check_out_time'] ?? null, $date);

        // Map status
        $status = $this->mapStatus($data['status'] ?? null, $checkInTime, $user);

        // Map type
        $type = $this->mapType($data['type'] ?? null);

        // Calculate work duration if not provided
        $workDuration = $data['work_duration'] ?? null;
        if (empty($workDuration) && $checkInTime && $checkOutTime) {
            $workDuration = Carbon::parse($checkInTime)->diffInMinutes(Carbon::parse($checkOutTime));
        }

        // Parse coordinates
        $checkInLat = $this->parseCoordinate($data['check_in_latitude'] ?? null);
        $checkInLong = $this->parseCoordinate($data['check_in_longitude'] ?? null);
        $checkOutLat = $this->parseCoordinate($data['check_out_latitude'] ?? null);
        $checkOutLong = $this->parseCoordinate($data['check_out_longitude'] ?? null);

        // If coordinates are empty but address contains BPKP/Kantor/Office keywords,
        // try to find matching location and apply random offset
        $checkInAddress = $data['check_in_address'] ?? null;
        if (empty($checkInLat) || empty($checkInLong)) {
            $locationCoords = $this->findLocationCoordinates($checkInAddress, $user->company_id);
            if ($locationCoords) {
                $checkInLat = $locationCoords['latitude'];
                $checkInLong = $locationCoords['longitude'];
            }
        }

        // Same for check-out coordinates
        $checkOutAddress = $data['check_out_address'] ?? $checkInAddress;
        if ((empty($checkOutLat) || empty($checkOutLong)) && $checkOutTime) {
            $locationCoords = $this->findLocationCoordinates($checkOutAddress, $user->company_id);
            if ($locationCoords) {
                $checkOutLat = $locationCoords['latitude'];
                $checkOutLong = $locationCoords['longitude'];
            }
        }

        // If ID exists, try to find existing record (UUID string)
        $existingId = !empty($data['id']) ? (string) $data['id'] : null;

        // Get device info from user's registered device
        $deviceInfo = \App\Models\UserDevice::getDeviceInfoForBackdate($user->id);

        // Find or create attendance
        return Attendance::updateOrCreate(
            [
                'user_id' => $user->id,
                'date' => $date,
            ],
            [
                'company_id' => $user->company_id,
                'type' => $type,
                'check_in_time' => $checkInTime,
                'check_in_latitude' => $checkInLat,
                'check_in_longitude' => $checkInLong,
                'check_in_address' => $data['check_in_address'] ?? null,
                'check_in_notes' => $data['check_in_notes'] ?? null,
                'check_out_time' => $checkOutTime,
                'check_out_latitude' => $checkOutLat,
                'check_out_longitude' => $checkOutLong,
                'check_out_address' => $data['check_out_address'] ?? null,
                'check_out_notes' => $data['check_out_notes'] ?? null,
                'work_duration' => $workDuration,
                'overtime_duration' => $data['overtime_duration'] ?? null,
                'status' => $status,
                // Device info dari user's registered device
                'check_in_device_model' => $deviceInfo['device_model'],
                'check_in_device_brand' => $deviceInfo['device_brand'],
                'check_in_os_version' => $deviceInfo['os_version'],
                'check_in_app_version' => $deviceInfo['app_version'],
                // Checkout device info jika ada checkout
                'check_out_device_model' => $checkOutTime ? $deviceInfo['device_model'] : null,
                'check_out_device_brand' => $checkOutTime ? $deviceInfo['device_brand'] : null,
                'check_out_os_version' => $checkOutTime ? $deviceInfo['os_version'] : null,
                'check_out_app_version' => $checkOutTime ? $deviceInfo['app_version'] : null,
            ]
        );
    }

    protected function parseDate(mixed $date): ?string
    {
        if (empty($date)) {
            return null;
        }

        // Handle Excel serial date number
        if (is_numeric($date) && $date > 1) {
            // Excel serial date: days since 1899-12-30 (with correction for Excel bug)
            $unixTimestamp = ($date - 25569) * 86400;
            return Carbon::createFromTimestamp($unixTimestamp)->format('Y-m-d');
        }

        try {
            return Carbon::parse($date)->format('Y-m-d');
        } catch (\Exception $e) {
            return null;
        }
    }

    protected function parseTime(mixed $time, string $date): ?string
    {
        if (empty($time)) {
            return null;
        }

        // Handle Excel numeric time format (e.g., 0.354166667 for 08:30)
        if (is_numeric($time) && $time < 1) {
            $totalSeconds = round($time * 86400);
            $hours = floor($totalSeconds / 3600);
            $minutes = floor(($totalSeconds % 3600) / 60);
            $seconds = $totalSeconds % 60;
            return Carbon::parse($date)->setTime($hours, $minutes, $seconds)->format('Y-m-d H:i:s');
        }

        // Handle Excel datetime numeric format (e.g., 45658.354166667)
        if (is_numeric($time) && $time > 1) {
            $unixTimestamp = ($time - 25569) * 86400;
            return Carbon::createFromTimestamp($unixTimestamp)->format('Y-m-d H:i:s');
        }

        $time = trim((string) $time);

        // If it's already a full datetime, parse it directly
        if (preg_match('/^\d{4}-\d{2}-\d{2}/', $time)) {
            return Carbon::parse($time)->format('Y-m-d H:i:s');
        }

        // Handle various time formats: HH:MM:SS, HH:MM, H:MM:SS, etc.
        if (preg_match('/^\d{1,2}:\d{2}(:\d{2})?/', $time)) {
            return Carbon::parse($date . ' ' . $time)->format('Y-m-d H:i:s');
        }

        // Try direct parse as fallback
        try {
            return Carbon::parse($date . ' ' . $time)->format('Y-m-d H:i:s');
        } catch (\Exception $e) {
            return null;
        }
    }

    protected function parseCoordinate(mixed $coord): ?float
    {
        if (empty($coord)) {
            return null;
        }

        // Remove any whitespace
        $coord = trim((string) $coord);

        if (is_numeric($coord)) {
            return (float) $coord;
        }

        return null;
    }

    protected function mapStatus(?string $status, ?string $checkInTime, User $user): string
    {
        if (empty($status)) {
            // Auto-determine based on check-in time
            if ($checkInTime) {
                $checkIn = Carbon::parse($checkInTime);
                $workStart = Carbon::parse($user->company?->work_start_time ?? '08:00:00');

                return $checkIn->gt($workStart) ? 'late' : 'present';
            }
            return 'present';
        }

        // Map Indonesian status to English (dari format export)
        return match (strtolower(trim($status))) {
            'hadir', 'present' => 'present',
            'terlambat', 'late' => 'late',
            'pulang awal', 'early_leave', 'early' => 'early_leave',
            'tidak hadir', 'absent' => 'absent',
            'sakit', 'sick' => 'sick',
            'cuti', 'leave' => 'leave',
            'izin', 'permit' => 'permit',
            default => 'present',
        };
    }

    protected function mapType(?string $type): string
    {
        if (empty($type)) {
            return 'office';
        }

        // Map dari format export Indonesia ke English
        return match (strtolower(trim($type))) {
            'kantor', 'office' => 'office',
            'remote', 'wfh' => 'remote',
            'dinas', 'business_trip' => 'business_trip',
            default => 'office',
        };
    }

    /**
     * Normalize nama untuk matching
     * - Lowercase
     * - Remove extra spaces
     * - Remove dots
     */
    protected function normalizeName(string $name): string
    {
        $name = strtolower(trim($name));
        $name = preg_replace('/\s+/', ' ', $name);  // Multiple spaces to single
        $name = str_replace('.', '', $name);        // Remove dots
        return $name;
    }

    /**
     * Find location coordinates from address keywords (BPKP, Kantor, Office, etc.)
     * Returns coordinates with random offset (5-30m) if found
     */
    protected function findLocationCoordinates(?string $address, int $companyId): ?array
    {
        if (empty($address)) {
            return null;
        }

        // Keywords that indicate office/headquarters location
        $keywords = ['BPKP Pusat', 'bpkp pusat', 'Office', 'office', 'Kantor', 'kantor', 'Headquarter', 'headquarter', 'HQ'];

        $matchesKeyword = false;
        foreach ($keywords as $keyword) {
            if (stripos($address, $keyword) !== false) {
                $matchesKeyword = true;
                break;
            }
        }

        if (!$matchesKeyword) {
            return null;
        }

        // Try to find matching location from database
        $location = \App\Models\Location::where('company_id', $companyId)
            ->where('is_active', true)
            ->where(function ($query) {
                $query->where('type', 'headquarters')
                    ->orWhere('is_primary', true)
                    ->orWhere('name', 'like', '%BPKP%')
                    ->orWhere('name', 'like', '%Kantor%');
            })
            ->whereNotNull('latitude')
            ->whereNotNull('longitude')
            ->first();

        if (!$location) {
            return null;
        }

        // Apply random offset using SuratTugas method for consistency
        return \App\Models\SuratTugas::applyRandomOffset(
            (float) $location->latitude,
            (float) $location->longitude
        );
    }

    public static function getCompletedNotificationBody(Import $import): string
    {
        $body = 'Import presensi selesai. ' . number_format($import->successful_rows) . ' baris berhasil diimport.';

        if ($failedRowsCount = $import->getFailedRowsCount()) {
            $body .= ' ' . number_format($failedRowsCount) . ' baris gagal.';
        }

        return $body;
    }
}
