using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using LSFE.MobApp.Contracts;
using LSFE.MobApp.Entities.Account;
using LSFE.MobApp.Helpers;
using LSFE.MobApp.Services;
using LSFE.MobApp.Services.Authentication;
using LSFE.MobApp.ViewModels.Common;
using Microsoft.Extensions.Logging;
using Microsoft.Maui.Graphics.Text;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LSFE.MobApp.ViewModels
{
    public partial class AttendanceVM : ObservableObject
    {
        private readonly IRepository<Attendance> _attendanceRepository;
        private readonly ILogger<AttendanceVM> _logger;
        private readonly ISyncService _syncService;
        private readonly AddressService _addressService;
        private bool _forAddressTranslation { get; set; }

        [ObservableProperty]
        private string currentTime;

        [ObservableProperty]
        private string currentDate;

        [ObservableProperty]
        private string locationAddress = "Fetching location...";

        [ObservableProperty]
        private string latitude;

        [ObservableProperty]
        private string longitude;

        [ObservableProperty]
        private ImageSource capturedImageIn;
        [ObservableProperty]
        private ImageSource capturedImageOut;

        [ObservableProperty]
        private bool hasTimeIn;

        [ObservableProperty]
        private bool hasTimeOut;

        [ObservableProperty]
        private string timeInText = "Not clocked in";

        [ObservableProperty]
        private string timeOutText = "Not clocked out";

        [ObservableProperty]
        private bool isLoading;
        [ObservableProperty]
        private bool hasImage;

        private Attendance _todayAttendance;

        public AttendanceVM(IRepository<Attendance> attendanceRepository, ILogger<AttendanceVM> logger,
            ISyncService syncService, AddressService addressService)
        {
            _addressService = addressService;
            _syncService = syncService;
            _attendanceRepository = attendanceRepository;
            _logger = logger;
            UpdateDateTime();
            // Update time every second
            Dispatcher.GetForCurrentThread()?.StartTimer(TimeSpan.FromSeconds(1), () =>
            {
                UpdateDateTime();
                return true;
            });
        }

        public async Task InitializeAsync()
        {
            await LoadTodayAttendanceAsync();
            await GetLocation();
        }

        private void UpdateDateTime()
        {
            var now = DateTime.Now;
            CurrentTime = now.ToString("hh:mm:ss tt");
            CurrentDate = now.ToString("MMMM dd, yyyy");
        }
        private async Task LoadTodayAttendanceAsync()
        {
            try
            {
                var today = DateTime.Today;
                // Get all attendance records for this user
                var query = @"SELECT * FROM Attendance 
                             WHERE UserId = ? 
                             AND TimeIn IS NOT NULL
                             ORDER BY TimeIn DESC";

                var cred = await UserCred.GetUserCred();
                var allResults = await _attendanceRepository.QueryAsync(query, cred.UserId);

                // Filter for today's record in memory
                _todayAttendance = allResults
                    .Where(a => a.TimeIn.HasValue && a.TimeIn.Value.Date == today)
                    .OrderByDescending(a => a.TimeIn)
                    .FirstOrDefault();


                if (_todayAttendance != null)
                {
                    HasTimeIn = _todayAttendance.TimeIn.HasValue;
                    HasTimeOut = _todayAttendance.TimeOut.HasValue;

                    if (HasTimeIn)
                        TimeInText = _todayAttendance.TimeIn.Value.ToString("hh:mm tt");

                    if (HasTimeOut)
                        TimeOutText = _todayAttendance.TimeOut.Value.ToString("hh:mm tt");

                    // Load saved image if exists
                    await LoadSavedImageAsync();

                    _logger.LogInformation("Loaded attendance: TimeIn={TimeIn}, TimeOut={TimeOut}, Image={Image}",
                        _todayAttendance.TimeIn, _todayAttendance.TimeOut, _todayAttendance.SignatureFileNameIn,
                        _todayAttendance.SignatureFileNameOut);
                }
                else
                {
                    // Reset values if no attendance found
                    HasTimeIn = false;
                    HasTimeOut = false;
                    TimeInText = "Not clocked in";
                    TimeOutText = "Not clocked out";
                    CapturedImageIn = null;
                    CapturedImageOut = null;
                    HasImage = false;
                    _todayAttendance = null;

                    _logger.LogInformation("No attendance record found for today");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error loading today's attendance");
                // Reset to safe state on error
                HasTimeIn = false;
                HasTimeOut = false;
                TimeInText = "Error loading data";
                TimeOutText = "Error loading data";
                CapturedImageIn = null;
                CapturedImageOut = null;
                HasImage = false;
            }
        }
        private async Task<string?> CaptureAndSavePhotoAsync(bool isTimeOut = false)
        {
            try
            {
                var photo = await MediaPicker.Default.CapturePhotoAsync(new MediaPickerOptions
                {
                    Title = isTimeOut ? "Take Time Out Photo" : "Take Time In Photo"
                });

                if (photo == null)
                    return null;

                // Generate a unique filename
                var fileName = $"{Guid.NewGuid()}{Path.GetExtension(photo.FileName)}";
                var localFilePath = Path.Combine(FileSystem.AppDataDirectory, fileName);

                // Save photo to local app directory
                using (var sourceStream = await photo.OpenReadAsync())
                using (var fileStream = File.Create(localFilePath))
                {
                    await sourceStream.CopyToAsync(fileStream);
                }

                // Convert to Base64 (for syncing or storage later)
                byte[] bytes = await File.ReadAllBytesAsync(localFilePath);
                string base64 = Convert.ToBase64String(bytes);

                // Save temporarily to Preferences
                if (isTimeOut)
                {
                    Preferences.Set("SignatureBase64Out", base64);
                    CapturedImageOut = ImageSource.FromFile(localFilePath);
                }
                else
                {
                    Preferences.Set("SignatureBase64In", base64);
                    CapturedImageIn = ImageSource.FromFile(localFilePath);
                }

                _logger.LogInformation("Saved {Type} photo locally: {FilePath}",
                    isTimeOut ? "Time-Out" : "Time-In", localFilePath);

                return fileName;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error capturing {Type} photo", isTimeOut ? "Time-Out" : "Time-In");
                await Shell.Current.DisplayAlert("Error", $"Failed to capture photo: {ex.Message}", "OK");
                return null;
            }
        }
        private async Task LoadSavedImageAsync()
        {
            try
            {
                if (_todayAttendance == null)
                {
                    CapturedImageIn = null;
                    CapturedImageOut = null;
                    return;
                }

                // --- Time In Photo ---
                if (!string.IsNullOrEmpty(_todayAttendance.SignatureFileNameIn))
                {
                    var pathIn = Path.Combine(FileSystem.AppDataDirectory, _todayAttendance.SignatureFileNameIn);
                    if (File.Exists(pathIn))
                    {
                        CapturedImageIn = ImageSource.FromFile(pathIn);
                    }
                    else
                    {
                        CapturedImageIn = null;
                        _logger.LogWarning("Time In image not found: {Path}", pathIn);
                    }
                }

                // --- Time Out Photo ---
                if (!string.IsNullOrEmpty(_todayAttendance.SignatureFileNameOut))
                {
                    var pathOut = Path.Combine(FileSystem.AppDataDirectory, _todayAttendance.SignatureFileNameOut);
                    if (File.Exists(pathOut))
                    {
                        CapturedImageOut = ImageSource.FromFile(pathOut);
                    }
                    else
                    {
                        CapturedImageOut = null;
                        _logger.LogWarning("Time Out image not found: {Path}", pathOut);
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error loading saved images");
                CapturedImageIn = null;
                CapturedImageOut = null;
            }
        }

        [RelayCommand]
        private async Task TakePhotoAsync()
        {
            try
            {
                bool isTimeOut = !string.IsNullOrEmpty(_todayAttendance?.SignatureFileNameIn) &&
                                 string.IsNullOrEmpty(_todayAttendance?.SignatureFileNameOut);

                var fileName = await CaptureAndSavePhotoAsync(isTimeOut);

                if (string.IsNullOrEmpty(fileName))
                {
                    _logger.LogWarning("No photo captured.");
                    return;
                }

                if (_todayAttendance == null)
                {
                    _logger.LogWarning("No attendance record found to update.");
                    return;
                }

                if (isTimeOut)
                {
                    _todayAttendance.SignatureFileNameOut = fileName;
                    CapturedImageOut = ImageSource.FromFile(Path.Combine(FileSystem.AppDataDirectory, fileName));
                }
                else
                {
                    _todayAttendance.SignatureFileNameIn = fileName;
                    CapturedImageIn = ImageSource.FromFile(Path.Combine(FileSystem.AppDataDirectory, fileName));
                }

                await _attendanceRepository.UpdateAsync(_todayAttendance);
                _logger.LogInformation("Updated attendance with {Type} photo: {FileName}", isTimeOut ? "Time-Out" : "Time-In", fileName);

                // Optional: immediately reload to show image in UI
                await LoadSavedImageAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error in TakePhotoAsync");
            }
        }

        [RelayCommand]
        private async Task TimeInAsync()
        {
            if (HasTimeIn)
            {
                await Shell.Current.DisplayAlert("Already Clocked In", "You have already clocked in today.", "OK");
                return;
            }

            try
            {
                IsLoading = true;

                // Step 1: Capture photo first
                var fileName = await CaptureAndSavePhotoAsync();

                if (string.IsNullOrEmpty(fileName))
                {
                    await UtilityVM.ShowAlert("Time-in", "A photo is required to continue the Time In process.");
                    return;
                }
                else
                {
                    // Step 2: Get location
                    await GetLocation();

                    var cred = await UserCred.GetUserCred();

                    // Step 3: Save attendance record
                    var attendance = new Attendance
                    {
                        AppAttendanceId = Guid.NewGuid(),
                        UserId = cred.UserId,
                        TimeIn = DateTime.Now,
                        TimeInLocation = LocationAddress,
                        Longitude = double.Parse(Longitude),
                        Latitude = double.Parse(Latitude),
                        SignatureFileNameIn = fileName,
                        ForAddressTranslation = _forAddressTranslation,

                    };

                    var result = await _attendanceRepository.SaveAsync(attendance);

                    if (result > 0)
                    {
                        // Reload to get the saved record with its ID
                        await LoadTodayAttendanceAsync();

                        await Shell.Current.DisplayAlert("Success",
                            "Time In recorded successfully!" +
                            (string.IsNullOrEmpty(fileName) ? "\n(No photo captured)" : ""),
                            "OK");

                        _logger.LogInformation("Time In recorded: {TimeIn} at {Location}, Photo: {Photo}",
                            attendance.TimeIn, attendance.TimeInLocation, fileName ?? "None");
                    }
                    else
                    {
                        throw new Exception("Failed to save attendance record");
                    }
                }              
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error recording time in");
                await Shell.Current.DisplayAlert("Error", $"Failed to record Time In: {ex.Message}", "OK");
            }
            finally
            {
                IsLoading = false;
            }
        }

        [RelayCommand]
        private async Task TimeOutAsync()
        {
            if (hasTimeOut)
            {
                await Shell.Current.DisplayAlert("Already Clocked Out", "You have already clocked out today.", "OK");
                return;
            }

            try
            {
                IsLoading = true;

                // Step 1: Capture photo first
                var fileName = await CaptureAndSavePhotoAsync(true);

                if (string.IsNullOrEmpty(fileName))
                {
                    await UtilityVM.ShowAlert("Time-out", "A photo is required to continue the Time Out process.");
                    return; 
                }
                else
                {
                    await GetLocation();

                    var cred = await UserCred.GetUserCred();

                    _todayAttendance.TimeOut = DateTime.Now;
                    _todayAttendance.TimeOutLocation = LocationAddress;
                    _todayAttendance.UserId = cred.UserId;
                    _todayAttendance.SignatureFileNameOut = fileName;
                    _todayAttendance.ForAddressTranslation = _forAddressTranslation;
                    var result = await _attendanceRepository.UpdateAsync(_todayAttendance);

                    if (result > 0)
                    {
                        HasTimeOut = true;
                        TimeOutText = _todayAttendance.TimeOut.Value.ToString("hh:mm tt");
                        // Reload to get the saved record with its ID
                        await LoadTodayAttendanceAsync();


                        await Shell.Current.DisplayAlert("Success", "Time Out recorded successfully!", "OK");

                        //_logger.LogInformation("Time Out recorded: {TimeOut} at {Location}",
                        //_todayAttendance.TimeOut, _todayAttendance.TimeOutLocation);
                    }
                    else
                    {
                        throw new Exception("Failed to update attendance record");
                    }
                }                        
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error recording time out");
                await Shell.Current.DisplayAlert("Error", $"Failed to record Time Out: {ex.Message}", "OK");
            }
            finally
            {
                IsLoading = false;
            }
        }

        [RelayCommand]
        private async Task SyncAsync()
        {
            IsLoading = true;

            await GetLocation();
            bool isSuccess = await _syncService.SyncPostAttendanceAsync();

            if (isSuccess)
                await UtilityVM.ShowAlert("Synchronization", "Synchronization successful.");
            else
                await UtilityVM.ShowAlert("Synchronization Failed", "Synchronization failed. Please check your signal and try again.");

            IsLoading = false;
        }
        private async Task GetLocation()
        {
            try
            {
                
                var (result,isReadableAddress) = await _addressService.GetCurrentLocationAsync(); 
                Latitude = result.Latitude.ToString("F6");
                Longitude = result.Longitude.ToString("F6");
                LocationAddress = result.Address;
                if (isReadableAddress)
                    _forAddressTranslation=false;
                else
                    _forAddressTranslation = true;
            }
            catch (Exception ex)
            {
                string message = ex.Message.Contains("permission", StringComparison.OrdinalIgnoreCase)
                    ? "Please allow location access in your device settings."
                    : ex.Message;

                await UtilityVM.ShowAlert("Location Error", message);
            }
        }
    }
}