using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Drawing.Printing;
using FrymasterBadgeApp.Services;
using Microsoft.Data.SqlClient;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Controls.Xaml;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Storage;

namespace FrymasterBadgeApp;

public partial class EmployeePage : ContentPage
{
    private readonly SqlService _db;
    private readonly PrinterService _printerService;
    private Dictionary<string, object>? _selectedEmployee;
    private readonly Dictionary<string, object> _currentCompany;

    // UI controls (explicitly declared and resolved via FindByName to avoid missing generated fields)
    // Use distinct field names to avoid ambiguity with generated XAML fields
    // UI controls are defined in XAML (x:Name) and provided by the generated partial class.
    // Avoid manual field declarations to prevent ambiguity with generated fields.

    private ObservableCollection<Dictionary<string, object>> _allEmployees = new();
    private ObservableCollection<Dictionary<string, object>> _filteredEmployees = new();

    private string _tempCapturePath = "";
    private double _editX = 0;
    private double _editY = 0;

    private string _currentBadgeType = "OFFICE";
    private string _currentGuestImage = "Guest1.png";

    // Paths from Settings
    private readonly string _photosBasePath;
    private readonly string _logosBasePath;
    private readonly string _imagesBasePath;

    private bool _showActiveOnly = true;

    public EmployeePage(
        SqlService db,
        PrinterService printerService,
        Dictionary<string, object> company
    )
    {
        // Use generated InitializeComponent (supports tooling & hot-reload) instead of loading XAML at runtime
        InitializeComponent();

        // Named XAML controls (x:Name) are available via generated partial class fields.
        // No manual FindByName assignments are necessary here.

        _db = db;
        _printerService = printerService;
        _currentCompany = company;
        _selectedEmployee = null;
        _photosBasePath = Preferences.Default.Get("PhotoBasePath", @"C:\FrymasterData\photos");
        _logosBasePath = Preferences.Default.Get("LogoBasePath", @"C:\FrymasterData\logos");
        _imagesBasePath = Preferences.Default.Get("ImagesBasePath", @"C:\FrymasterData\images");

        EnsureDirectoryExists(_photosBasePath);
        EnsureDirectoryExists(_logosBasePath);
        EnsureDirectoryExists(_imagesBasePath);

        // Use FindByName to locate the checkbox from XAML in case generated field isn't available
        var activeCheck = this.FindByName<CheckBox>("ActiveFilterCheckBox");
        if (activeCheck != null)
        {
            activeCheck.IsChecked = _showActiveOnly;
            activeCheck.CheckedChanged += OnActiveFilterChanged;
        }
        else
        {
            AppLogger.Warning("ActiveFilterCheckBox not found in XAML.");
        }

        if (EditorPhotoPreview != null && ZoomSlider != null)
        {
            EditorPhotoPreview.SetBinding(
                Image.ScaleProperty,
                new Binding("Value", source: ZoomSlider)
            );
        }

        _editX = _editY = 0;
        if (EditorPhotoPreview != null)
        {
            EditorPhotoPreview.TranslationX = 0;
            EditorPhotoPreview.TranslationY = 0;
        }
        if (ZoomSlider != null)
            ZoomSlider.Value = 1;

        if (GuestImagePicker != null)
        {
            GuestImagePicker.Items.Add("Guest1.png");
            GuestImagePicker.SelectedIndex = 0;
            GuestImagePicker.SelectedIndexChanged += OnGuestImageChanged;
        }

        if (BadgeTypePicker != null)
            BadgeTypePicker.SelectedIndexChanged += OnBadgeTypeChanged;

        if (SearchResultsList != null)
            SearchResultsList.ItemsSource = _filteredEmployees;

        LoadCompanyLogo();
        LoadEmployees();

        ShowNoSelectionMessage();
    }

    private void ShowNoSelectionMessage()
    {
        PreviewFrame.Content = new VerticalStackLayout
        {
            VerticalOptions = LayoutOptions.Center,
            HorizontalOptions = LayoutOptions.Center,
            Spacing = 10,
            Children =
            {
                new Label
                {
                    Text = "Select an employee",
                    FontSize = 24,
                    TextColor = Colors.Gray,
                    FontAttributes = FontAttributes.Italic,
                },
                new Label
                {
                    Text = "to view badge preview",
                    FontSize = 16,
                    TextColor = Colors.LightGray,
                },
            },
        };
    }

    private static void EnsureDirectoryExists(string path)
    {
        if (string.IsNullOrWhiteSpace(path) || Directory.Exists(path))
            return;
        try
        {
            Directory.CreateDirectory(path);
        }
        catch (Exception ex)
        {
            AppLogger.Error($"Failed to create directory: {path}", ex);
        }
    }

    /// <summary>
    /// Resolves full logo path: filename from Companies table + folder from Settings
    /// </summary>
    private string GetCompanyLogoPath()
    {
        string fileName = _currentCompany.GetValueOrDefault("Logo")?.ToString()?.Trim();
        if (string.IsNullOrWhiteSpace(fileName))
        {
            AppLogger.Info("No logo filename in company data");
            return string.Empty;
        }

        string fullPath = Path.Combine(_logosBasePath, fileName);

        if (File.Exists(fullPath))
        {
            AppLogger.Info($"Company logo found: {fullPath}");
            return fullPath;
        }

        AppLogger.Warning($"Company logo missing at: {fullPath}");
        return string.Empty;
    }

    private void LoadCompanyLogo()
    {
        string logoPath = GetCompanyLogoPath();
    }

    private string GetPhotoPath(string picCode)
    {
        return Path.Combine(_photosBasePath, $"{picCode}.jpg");
    }

    private string GetGuestImagePath(string guestImageName)
    {
        return Path.Combine(_imagesBasePath, guestImageName);
    }

    protected override async void OnAppearing()
    {
        base.OnAppearing();

        // This effectively "closes" the previous state
        ResetPageToDefault();

        LoadAvailablePrinters();
        // Then re-loads the data fresh from the DB
        await LoadEmployeesAsync();
    }

    private void ResetPageToDefault()
    {
        // 1. Wipe the selection reference
        _selectedEmployee = null;

        // 2. Clear the UI Search Bar text
        if (EmployeeSearchBar != null)
        {
            // We unsubscribe to prevent 'ApplyFilters' from running
            // while we are manually clearing the text.
            EmployeeSearchBar.TextChanged -= OnSearchTextChanged;
            EmployeeSearchBar.Text = string.Empty;
            EmployeeSearchBar.TextChanged += OnSearchTextChanged;
        }

        // 3. Reset the visual results list
        if (SearchResultsList != null)
        {
            SearchResultsList.SelectedItem = null;
            // SearchResultsList.IsVisible = false;
            _filteredEmployees.Clear();
        }

        // 4. Reset the Preview to the placeholder message
        ShowNoSelectionMessage();

        AppLogger.Info("EmployeePage: UI Reset completed.");
    }

    private async Task LoadEmployeesAsync()
    {
        try
        {
            // We do not filter by company here — load all employees
            var results = await Task.Run(() =>
                _db.Query(
                    "SELECT *, ISNULL(BadgeType, 'OFFICE') AS BadgeType FROM dbo.tblData ORDER BY Data2",
                    null
                )
            );

            MainThread.BeginInvokeOnMainThread(() =>
            {
                _allEmployees.Clear();
                foreach (var emp in results)
                {
                    _allEmployees.Add(emp);
                }

                // Re-run ApplyFilters if there is already text in the search bar
                ApplyFilters();

                AppLogger.Info(
                    $"EmployeePage: Loaded {_allEmployees.Count} records for {_currentCompany.GetValueOrDefault("Name")}"
                );
            });
        }
        catch (Exception ex)
        {
            AppLogger.Error("EmployeePage: Load failed", ex);
        }
    }

    private void LoadAvailablePrinters()
    {
        if (PrinterPicker != null)
        {
            PrinterPicker.Items.Clear();
            foreach (string printer in PrinterSettings.InstalledPrinters)
                PrinterPicker.Items.Add(printer);

            if (PrinterPicker.Items.Count > 0)
                PrinterPicker.SelectedIndex = 0;
        }
    }

    private void OnRefreshPrintersClicked(object sender, EventArgs e) => LoadAvailablePrinters();

    private void OnActiveFilterChanged(object sender, CheckedChangedEventArgs e)
    {
        _showActiveOnly = e.Value;
        ApplyFilters();
    }

    private void OnSearchTextChanged(object sender, TextChangedEventArgs e)
    {
        PositionSearchResultsDropdown();
        SearchResultsList.IsVisible = true;
        ApplyFilters();
    }

    private void ApplyFilters()
    {
        string filter = EmployeeSearchBar?.Text?.Trim()?.ToLower() ?? "";

        var filtered = _allEmployees.AsEnumerable();

        if (_showActiveOnly)
            filtered = filtered.Where(emp =>
                string.Equals(
                    emp.GetValueOrDefault("Active")?.ToString(),
                    "YES",
                    StringComparison.OrdinalIgnoreCase
                )
            );

        if (!string.IsNullOrEmpty(filter))
            filtered = filtered.Where(emp =>
                emp["Data2"]?.ToString()?.ToLower().Contains(filter) == true
                || emp["Data1"]?.ToString()?.Contains(filter) == true
            );

        var finalList = filtered.ToList();

        MainThread.BeginInvokeOnMainThread(() =>
        {
            _filteredEmployees.Clear();
            foreach (var emp in finalList)
                _filteredEmployees.Add(emp);
            if (SearchResultsList != null)
            {
                SearchResultsList.ItemsSource = _filteredEmployees;
///           SearchResultsList.IsVisible = finalList.Any();
            }
        });
    }

        /// <summary>
        /// Loads the list of employees from the database.
        /// The results are ordered by employee name (ascending).
        /// </summary>
    private void LoadEmployees()
    {
        string sql =
            "SELECT *, ISNULL(BadgeType, 'OFFICE') AS BadgeType FROM dbo.tblData ORDER BY Data2";
        var results = _db.Query(sql, null);

        MainThread.BeginInvokeOnMainThread(() =>
        {
            _allEmployees.Clear();
            foreach (var emp in results)
                _allEmployees.Add(emp);
            // ApplyFilters();
        });
    }

        /// <summary>
        /// Positions the search results dropdown below the search bar.
        /// </summary>
    private void PositionSearchResultsDropdown()
    {
        if (SearchResultsList != null)
        {
            SearchResultsList.TranslationY = 60;
            SearchResultsList.ZIndex = 100;
        }
    }

        /// <summary>
        /// Called when the selection in the employees list changes.
        /// Sets the selected employee, updates the search bar text, resolves the badge type with case sensitivity handling, and updates the UI accordingly.
        /// </summary>
        /// <param name="sender">The object that invoked this method.</param>
        /// <param name="e">The event data for this method.</param>
    private void OnEmployeeSelected(object sender, SelectedItemChangedEventArgs e)
    {
        if (e.SelectedItem is not Dictionary<string, object> selectedEmp)
            return;

        _selectedEmployee = selectedEmp;

        // 1. Update Search Bar text
        if (EmployeeSearchBar != null)
            EmployeeSearchBar.Text = selectedEmp.GetValueOrDefault("Data2")?.ToString();

        // 2. Resolve Badge Type with Case Sensitivity handling
        string dbValue = selectedEmp.GetValueOrDefault("BadgeType")?.ToString()?.Trim() ?? "OFFICE";

        var matchingItem = BadgeTypePicker?.Items.FirstOrDefault(i =>
            string.Equals(i, dbValue, StringComparison.OrdinalIgnoreCase)
        );

        if (matchingItem != null && BadgeTypePicker != null)
        {
            BadgeTypePicker.SelectedItem = matchingItem;
            _currentBadgeType = matchingItem; // Sync our internal variable
        }
        else if (BadgeTypePicker != null)
        {
            BadgeTypePicker.SelectedItem = "OFFICE";
            _currentBadgeType = "OFFICE";
        }

        if (GuestImageSelector != null)
            GuestImageSelector.IsVisible = _currentBadgeType.Equals(
                "GUEST11",
                StringComparison.OrdinalIgnoreCase
            );

        RenderBadgePreview(selectedEmp);
        if (SearchResultsList != null)
            SearchResultsList.IsVisible = false;

        if (sender is ListView lv)
            lv.SelectedItem = null;
    }

        /// <summary>
        /// Called when the "Add Employee" button is clicked.
        /// Shows a modal form for adding a new employee.
        /// The form is given a callback to refresh and select the new employee when it is saved.
        /// </summary>
        /// <param name="sender">The object that invoked this method.</param>
        /// <param name="e">The event data for this method.</param>
    private async void OnAddEmployeeClicked(object sender, EventArgs e)
    {
        try
        {
            var form = new EmployeeFormPage(_db);

            // Set the callback to refresh and select the new employee
            form.OnSavedCallback = async (savedEmployee) =>
            {
                await LoadEmployeesAsync();
                SelectAndShowEmployee(savedEmployee);
            };

            await Navigation.PushModalAsync(form);
        }
        catch (Exception ex)
        {
            await DisplayAlert("Error", ex.Message, "OK");
        }
    }

    /// <summary>
    /// Helper to find the employee in the list, highlight it, and scroll to it.
    /// </summary>
    private void SelectAndShowEmployee(Dictionary<string, object> target)
    {
        MainThread.BeginInvokeOnMainThread(() =>
        {
            var recordId = target.GetValueOrDefault("Data1")?.ToString();

            // Find the item in our collection
            var itemToSelect = _allEmployees.FirstOrDefault(x =>
                x.GetValueOrDefault("Data1")?.ToString() == recordId
            );

            if (itemToSelect != null)
            {
                _selectedEmployee = itemToSelect;

                // Clear search bar so the item is visible in the list
                if (EmployeeSearchBar != null)
                    EmployeeSearchBar.Text = itemToSelect.GetValueOrDefault("Data2")?.ToString();

                // Update the Preview
                RenderBadgePreview(itemToSelect);

                // Note: Since you are using a custom dropdown SearchResultsList,
                // we ensure it updates its selection visually
                if (SearchResultsList != null)
                {
                    SearchResultsList.SelectedItem = itemToSelect;
                    // Note: ListView.ScrollTo requires the item, Group (null), and Position
                    SearchResultsList.ScrollTo(itemToSelect, ScrollToPosition.Center, true);
                }
            }
        });
    }

        /// <summary>
        /// Opens the EmployeeFormPage for editing the selected employee.
        /// If the form is saved, it refreshes the list and shows the updated employee.
        /// </summary>
        /// <param name="sender">The object that invoked this method.</param>
        /// <param name="e">The event data for this method.</param>
    private async void OnEditEmployeeClicked(object sender, EventArgs e)
    {
        if (_selectedEmployee == null)
        {
            await DisplayAlert("No Selection", "Please select an employee first.", "OK");
            return;
        }

        try
        {
            var formPage = new EmployeeFormPage(_db, _selectedEmployee);
            await Navigation.PushModalAsync(formPage);
            if (formPage.IsSaved)
            {
                LoadEmployees();
                RenderBadgePreview(_selectedEmployee);
            }
        }
        catch (Exception ex)
        {
            await DisplayAlert("Error", ex.Message, "OK");
        }
    }

        /// <summary>
        /// Called when the "Badge Type" dropdown changes.
        /// Saves the new badge type to the database and updates the badge preview.
        /// </summary>
        /// <param name="sender">The object that invoked this method.</param>
        /// <param name="e">The event data for this method.</param>
    private void OnBadgeTypeChanged(object sender, EventArgs e)
    {
        if (_selectedEmployee == null)
            return;

        _currentBadgeType = BadgeTypePicker?.SelectedItem?.ToString() ?? "OFFICE";
        if (GuestImageSelector != null)
            GuestImageSelector.IsVisible = _currentBadgeType == "GUEST";

        string sql = "UPDATE dbo.tblData SET BadgeType = @type WHERE Data1 = @id";
        _db.Execute(
            sql,
            new[]
            {
                new SqlParameter("@type", _currentBadgeType),
                new SqlParameter("@id", _selectedEmployee["Data1"]),
            }
        );

        _selectedEmployee["BadgeType"] = _currentBadgeType;
        RenderBadgePreview(_selectedEmployee);
    }

/// <summary>
/// Called when the "Guest Image" dropdown changes.
/// Updates the preview of the badge if the badge type is GUEST.
/// </summary>
/// <param name="sender">The object that invoked this method.</param>
/// <param name="e">The event data for this method.</param>
    private void OnGuestImageChanged(object sender, EventArgs e)
    {
        _currentGuestImage = GuestImagePicker?.SelectedItem?.ToString() ?? "Guest1.png";
        if (_selectedEmployee != null && _currentBadgeType == "GUEST")
            RenderBadgePreview(_selectedEmployee);
    }

/// <summary>
/// Deploys the default images (Guest.png and WelBilt.png) if they are not already present in the images directory.
/// </summary>
    private async Task InitialiseDefaultImages()
{
    string[] defaultFiles = { "Guest.png", "WelBilt.png" };

    foreach (var fileName in defaultFiles)
    {
        string targetPath = Path.Combine(_imagesBasePath, fileName);

        if (!File.Exists(targetPath))
        {
            try
            {
                // This looks into your Resources\Raw folder
                using var stream = await FileSystem.OpenAppPackageFileAsync(fileName);
                using var memoryStream = new MemoryStream();
                await stream.CopyToAsync(memoryStream);

                File.WriteAllBytes(targetPath, memoryStream.ToArray());
                AppLogger.Info($"Deployed default image: {fileName} to {targetPath}");
            }
            catch (Exception ex)
            {
                AppLogger.Error($"Could not deploy default image {fileName}", ex);
            }
        }
    }
}

/// <summary>
/// Renders the preview for a badge given the employee's data.
/// </summary>
/// <param name="emp">The employee's data.</param>
    private void RenderBadgePreview(Dictionary<string, object> emp)
    {
        string companyName = _currentCompany.GetValueOrDefault("Name")?.ToString() ?? "Frymaster";
        string logoPath = GetCompanyLogoPath(); // ← correct company logo resolution

        string formattedAddress =
            $"{_currentCompany.GetValueOrDefault("Address")}\n"
            + $"{_currentCompany.GetValueOrDefault("City")}, "
            + $"{_currentCompany.GetValueOrDefault("State")} {_currentCompany.GetValueOrDefault("Zip")}";

        string recordNum = (emp.GetValueOrDefault("Data1")?.ToString() ?? "").Trim();
        string barcode = (emp.GetValueOrDefault("Data9")?.ToString() ?? "").Trim();
        string date = (emp.GetValueOrDefault("Data5")?.ToString() ?? "").Trim();

        var mainLayout = new HorizontalStackLayout
        {
            Spacing = 40,
            HorizontalOptions = LayoutOptions.Center,
        };
        var frontLayout = new VerticalStackLayout { Spacing = 5 };
        var backLayout = new VerticalStackLayout
        {
            Spacing = 0,
            VerticalOptions = LayoutOptions.Fill,
        };

        var frontFrame = new Frame
        {
            Padding = 15,
            WidthRequest = 300,
            HeightRequest = 480,
            BackgroundColor = Colors.White,
            BorderColor = Colors.White,
            CornerRadius = 15,
            HasShadow = true,
            Content = frontLayout,
        };

        var backFrame = new Frame
        {
            WidthRequest = 480,
            HeightRequest = 300,
            BackgroundColor = Colors.White,
            BorderColor = Colors.White,
            CornerRadius = 15,
            Padding = 0,
            HasShadow = true,
            Content = backLayout,
        };

        bool medicalBlue = string.Equals(
            emp.GetValueOrDefault("Data7")?.ToString(),
            "True",
            StringComparison.OrdinalIgnoreCase
        );
        bool medicalRed = string.Equals(
            emp.GetValueOrDefault("Data8")?.ToString(),
            "True",
            StringComparison.OrdinalIgnoreCase
        );

        switch (_currentBadgeType)
        {
            case "GUEST":
                frontFrame.BorderColor = Colors.Purple;

                // 1. Company Logo
                if (!string.IsNullOrEmpty(logoPath))
                {
                    frontLayout.Children.Add(new Image
                    {
                        Source = ImageSource.FromFile(logoPath),
                        HeightRequest = 55,
                        Margin = new Thickness(0, 0, 0, 10),
                    });
                }

                // 2. Resolve Path
                string guestImg = _currentGuestImage ?? "Guest.jpg";

                var guestImageControl = new Image
                {
                    HeightRequest = 220,
                    WidthRequest = 220,
                    Aspect = Aspect.AspectFit,
                    HorizontalOptions = LayoutOptions.Center,
                    Margin = new Thickness(0, 20),
                    BackgroundColor = Colors.White // Keep this until it works!
                };

                string guestPath = GetGuestImagePath(_currentGuestImage);

                // Add this line temporarily:
                //DisplayAlert("Path Check", $"Path: {guestPath}\nExists: {File.Exists(guestPath)}", "OK");
                // 3. Robust Stream Loading
                if (File.Exists(guestPath))
                {
                    // Loading via stream bypasses many "FromFile" permission issues
                    guestImageControl.Source = ImageSource.FromStream(() => File.OpenRead(guestPath));
                }
                else
                {
                    // Fallback so you know the file is physically missing from the disk
                    guestImageControl.Source = "dotnet_bot.png";
                }

                frontLayout.Children.Add(guestImageControl);
                AddStandardBackDetails(backLayout, companyName, formattedAddress, recordNum, date, false, logoPath, medicalBlue, medicalRed);
                break;

            case "PLANT":
            case "MAINTENANCE": // "Maintenance":
                Color stripeColor = (_currentBadgeType == "PLANT") ? Colors.Red : Colors.Yellow;
                frontFrame.BorderColor =
                    (_currentBadgeType == "PLANT") ? Colors.Green : Colors.Orange;

                AddStandardFront(
                    frontLayout,
                    logoPath,
                    emp,
                    recordNum,
                    "",
                    medicalBlue,
                    medicalRed
                );

                var stripeGrid = new Grid
                {
                    BackgroundColor = stripeColor,
                    HorizontalOptions = LayoutOptions.Fill,
                    HeightRequest = 45,
                    Margin = new Thickness(0, 35, 0, 0),
                };
                stripeGrid.Children.Add(
                    new Label
                    {
                        Text = barcode,
                        FontFamily = "BarcodeFont",
                        FontSize = 75,
                        Padding = 0,
                        TextColor = Colors.Black,
                        HorizontalTextAlignment = TextAlignment.Center,
                        VerticalTextAlignment = TextAlignment.Center,
                        LineHeight = 0.8,
                        Margin = new Thickness(0, 0, 0, -25),
                    }
                );
                backLayout.Children.Add(stripeGrid);

                AddStandardBackDetails(
                    backLayout,
                    companyName,
                    formattedAddress,
                    recordNum,
                    date,
                    false,
                    logoPath,
                    medicalBlue,
                    medicalRed
                );
                break;

            case "VENDOR":
                frontFrame.BorderColor = Colors.Red;
                AddStandardFront(frontLayout, logoPath, emp, "", "VENDOR", medicalBlue, medicalRed);
                AddStandardBackDetails(
                    backLayout,
                    companyName,
                    formattedAddress,
                    recordNum,
                    date,
                    false,
                    logoPath,
                    medicalBlue,
                    medicalRed
                );
                break;

            case "OFFICE":
                frontFrame.BorderColor = Colors.Blue;
                AddStandardFront(
                    frontLayout,
                    logoPath,
                    emp,
                    recordNum,
                    "",
                    medicalBlue,
                    medicalRed
                );

                AddStandardBackDetails(
                    backLayout,
                    companyName,
                    formattedAddress,
                    recordNum,
                    date,
                    true,
                    logoPath,
                    medicalBlue,
                    medicalRed
                );
                break;
        }

        mainLayout.Children.Add(frontFrame);
        mainLayout.Children.Add(backFrame);

        MainThread.BeginInvokeOnMainThread(() => PreviewFrame.Content = mainLayout);
    }

/// <summary>
/// Adds a standard front to the badge layout, including the logo (if supplied), employee photo, and name.
/// </summary>
/// <param name="layout">The layout to add the front to.</param>
/// <param name="logoPath">The path to the logo image.</param>
/// <param name="emp">The employee data.</param>
/// <param name="recordNum">The record number to display on the front.</param>
/// <param name="specialFooter">The special footer text to display on the front.</param>
/// <param name="medicalBlue">Whether to display a blue medical symbol on the front.</param>
/// <param name="medicalRed">Whether to display a red medical symbol on the front.</param>
    private void AddStandardFront(
        VerticalStackLayout layout,
        string logoPath,
        Dictionary<string, object> emp,
        string recordNum,
        string specialFooter,
        bool medicalBlue = false,
        bool medicalRed = false
    )
    {
        if (!string.IsNullOrEmpty(logoPath))
            layout.Children.Add(
                new Image
                {
                    Source = ImageSource.FromFile(logoPath),
                    HeightRequest = 55,
                    Margin = new Thickness(0, 0, 0, 10),
                }
            );

        string picCode = emp.GetValueOrDefault("picCode")?.ToString() ?? "";
        string photoPath = GetPhotoPath(picCode);

        var photoContainer = new Grid
        {
            HeightRequest = 180,
            WidthRequest = 180,
            IsClippedToBounds = true,
            HorizontalOptions = LayoutOptions.Center,
            VerticalOptions = LayoutOptions.Center,
        };

        var photoImg = new Image
        {
            Aspect = Aspect.AspectFill,
            HorizontalOptions = LayoutOptions.Fill,
            VerticalOptions = LayoutOptions.Fill,
        };

        if (File.Exists(photoPath))
        {
            photoImg.Source = ImageSource.FromFile(photoPath);
            double scale = SafeToDouble(emp.GetValueOrDefault("cropScale"), 1.0);
            double cropX = SafeToDouble(emp.GetValueOrDefault("cropX"), 0.0);
            double cropY = SafeToDouble(emp.GetValueOrDefault("cropY"), 0.0);

            photoImg.Scale = scale;
            photoImg.TranslationX = cropX;
            photoImg.TranslationY = cropY;
        }

        photoContainer.Children.Add(photoImg);
        layout.Children.Add(photoContainer);

        layout.Children.Add(
            new Label
            {
                Text = (emp.GetValueOrDefault("Data3")?.ToString() ?? "").ToUpper(),
                FontSize = 36,
                FontAttributes = FontAttributes.Bold,
                TextColor = Colors.Black,
                HorizontalTextAlignment = TextAlignment.Center,
            }
        );

        layout.Children.Add(
            new Label
            {
                Text = (emp.GetValueOrDefault("Data2")?.ToString() ?? ""),
                FontSize = 18,
                TextColor = Colors.Black,
                HorizontalTextAlignment = TextAlignment.Center,
            }
        );

        layout.Children.Add(
            new Label
            {
                Text = "*" + (recordNum ?? "") + "*",
                FontFamily = "BarcodeFont",
                FontSize = 45,
                TextColor = Colors.Black,
                HorizontalTextAlignment = TextAlignment.Center,
            }
        );

        if (medicalBlue || medicalRed)
        {
            var medStack = new HorizontalStackLayout
            {
                HorizontalOptions = LayoutOptions.Center,
                Spacing = 20,
            };
            if (medicalBlue)
                medStack.Children.Add(
                    new Label
                    {
                        Text = "*",
                        FontSize = 48,
                        FontAttributes = FontAttributes.Bold,
                        TextColor = Colors.Blue,
                    }
                );
            if (medicalRed)
                medStack.Children.Add(
                    new Label
                    {
                        Text = "*",
                        FontSize = 48,
                        FontAttributes = FontAttributes.Bold,
                        TextColor = Colors.Red,
                    }
                );
            layout.Children.Add(medStack);
        }
    }

        /// <summary>
        /// Adds a standard set of details to the back of the badge preview,
        /// including company name, address, ID, date, and medical indicators.
        /// </summary>
        /// <param name="layout">The layout to add the details to.</param>
        /// <param name="company">The name of the company.</param>
        /// <param name="address">The address of the company.</param>
        /// <param name="id">The ID of the badge.</param>
        /// <param name="date">The date the badge was issued.</param>
        /// <param name="showLogo">Whether to show the company logo.</param>
        /// <param name="logoPath">The path to the company logo.</param>
        /// <param name="medicalBlue">Whether to show the medical blue indicator.</param>
        /// <param name="medicalRed">Whether to show the medical red indicator.</param>
    private void AddStandardBackDetails(
        VerticalStackLayout layout,
        string company,
        string address,
        string id,
        string date,
        bool showLogo,
        string logoPath,
        bool medicalBlue,
        bool medicalRed
    )
    {
        var textContainer = new VerticalStackLayout { Padding = 20, Spacing = 5 };

        if (showLogo && !string.IsNullOrEmpty(logoPath))
            textContainer.Children.Add(
                new Image
                {
                    Source = ImageSource.FromFile(logoPath),
                    HeightRequest = 40,
                    Margin = new Thickness(0, 10, 0, 5),
                }
            );

        textContainer.Children.Add(
            new Label
            {
                Text = $"This badge is property of {company}",
                FontAttributes = FontAttributes.Bold,
                FontSize = 16,
                HorizontalTextAlignment = TextAlignment.Center,
                TextColor = Colors.Blue,
            }
        );

        textContainer.Children.Add(
            new Label
            {
                Text = "If found, please return to Human Resources.",
                FontSize = 16,
                HorizontalTextAlignment = TextAlignment.Center,
                TextColor = Colors.Black,
            }
        );

        textContainer.Children.Add(
            new Label
            {
                Text = address,
                FontSize = 16,
                TextColor = Colors.DarkSlateGray,
                HorizontalTextAlignment = TextAlignment.Center,
            }
        );

        textContainer.Children.Add(
            new BoxView
            {
                Color = Colors.LightGray,
                HeightRequest = 1,
                Margin = new Thickness(20, 10),
            }
        );

        var grid = new Grid
        {
            ColumnDefinitions =
            {
                new ColumnDefinition { Width = GridLength.Auto }, // Left side fits the ID
                new ColumnDefinition { Width = GridLength.Star }, // Right side takes remaining space
            },
            WidthRequest = 440, // Reduced slightly to ensure it fits inside the 480 frame padding
            Margin = new Thickness(20, 0),
        };

        // First Label (Column 0)
        var idLabel = new Label
        {
            Text = id,
            FontAttributes = FontAttributes.Bold,
            FontSize = 16,
            TextColor = Colors.Black,
            VerticalTextAlignment = TextAlignment.Center,
        };
        Grid.SetColumn(idLabel, 0); // <--- Explicitly set column 0

        // Second Label (Column 1)
        var issuedLabel = new Label
        {
            Text = $"Issued: {date}",
            FontSize = 16,
            TextColor = Colors.Black,
            HorizontalOptions = LayoutOptions.End,
            HorizontalTextAlignment = TextAlignment.End,
            VerticalTextAlignment = TextAlignment.Center,
        };
        Grid.SetColumn(issuedLabel, 1); // <--- Explicitly set column 1

        // Add them to the grid
        grid.Children.Add(idLabel);
        grid.Children.Add(issuedLabel);

        textContainer.Children.Add(grid);

        if (medicalBlue || medicalRed)
        {
            var medStack = new HorizontalStackLayout
            {
                HorizontalOptions = LayoutOptions.Center,
                Spacing = 30,
            };
            if (medicalBlue)
                medStack.Children.Add(
                    new Label
                    {
                        Text = "*",
                        FontSize = 40,
                        FontAttributes = FontAttributes.Bold,
                        TextColor = Colors.Blue,
                    }
                );
            if (medicalRed)
                medStack.Children.Add(
                    new Label
                    {
                        Text = "*",
                        FontSize = 40,
                        FontAttributes = FontAttributes.Bold,
                        TextColor = Colors.Red,
                    }
                );
            textContainer.Children.Add(medStack);
        }

        layout.Children.Add(textContainer);
    }

        /// <summary>
        /// Called when the "Take Photo" button is clicked.
        /// Opens the camera and takes a photo. If successful, updates the photo preview and resets the editor state.
        /// </summary>
        /// <param name="sender">The object that invoked this method.</param>
        /// <param name="e">The event data for this method.</param>
        /// <exception cref="Exception">Thrown if there is an error taking the photo.</exception>
    private async void OnTakePhotoClicked(object sender, EventArgs e)
    {
        if (_selectedEmployee == null)
            return;

        try
        {
            var photo = await MediaPicker.Default.CapturePhotoAsync();
            if (photo == null)
                return;

            _tempCapturePath = Path.Combine(FileSystem.CacheDirectory, "temp_editor.jpg");

            using var source = await photo.OpenReadAsync();
            using var destination = File.OpenWrite(_tempCapturePath);
            await source.CopyToAsync(destination);

            if (EditorPhotoPreview != null)
                EditorPhotoPreview.Source = ImageSource.FromFile(_tempCapturePath);
            _editX = _editY = 0;
            if (EditorPhotoPreview != null)
            {
                EditorPhotoPreview.TranslationX = 0;
                EditorPhotoPreview.TranslationY = 0;
            }
            if (ZoomSlider != null)
                ZoomSlider.Value = 1;

            if (PhotoEditorOverlay != null)
                PhotoEditorOverlay.IsVisible = true;
        }
        catch (Exception ex)
        {
            await DisplayAlert("Camera Error", ex.Message, "OK");
        }
    }

        /// <summary>
        /// Called when the user is panning the photo.
        /// </summary>
        /// <param name="sender">The object that invoked this method.</param>
        /// <param name="e">The event data for this method.</param>
        /// <remarks>
        /// If the pan is running, updates the translation of the photo based on the total pan distance.
        /// If the pan is completed, updates the stored edit position based on the final translation of the photo.
        /// </remarks>
    private void OnPanUpdated(object sender, PanUpdatedEventArgs e)
    {
        if (e.StatusType == GestureStatus.Running)
        {
            if (EditorPhotoPreview != null)
            {
                EditorPhotoPreview.TranslationX = _editX + e.TotalX;
                EditorPhotoPreview.TranslationY = _editY + e.TotalY;
            }
        }
        else if (e.StatusType == GestureStatus.Completed)
        {
            if (EditorPhotoPreview != null)
            {
                _editX = EditorPhotoPreview.TranslationX;
                _editY = EditorPhotoPreview.TranslationY;
            }
        }
    }

        /// <summary>
        /// Called when the user wants to apply the edited photo to the record.
        /// </summary>
        /// <param name="sender">The object that invoked this method.</param>
        /// <param name="e">The event data for this method.</param>
        /// <remarks>
        /// Copies the edited photo to the final location, updates the database, and renders the badge preview.
        /// </remarks>
    private async void OnApplyPhoto(object sender, EventArgs e)
    {
        if (_selectedEmployee == null)
            return;

        try
        {
            string recordNum =
                _selectedEmployee.GetValueOrDefault("Data1")?.ToString()?.Trim() ?? "";
            string picCode = $"{recordNum}_{DateTime.Now.Ticks}";
            string finalPath = Path.Combine(_photosBasePath, $"{picCode}.jpg");

            File.Copy(_tempCapturePath, finalPath, true);

            string sql =
                @"UPDATE dbo.tblData SET picCode = @pic, cropScale = @s, cropX = @x, cropY = @y WHERE Data1 = @id";
            _db.Execute(
                sql,
                new[]
                {
                    new SqlParameter("@pic", picCode),
                    new SqlParameter("@s", ZoomSlider?.Value ?? 1.0),
                    new SqlParameter("@x", EditorPhotoPreview?.TranslationX ?? 0.0),
                    new SqlParameter("@y", EditorPhotoPreview?.TranslationY ?? 0.0),
                    new SqlParameter("@id", recordNum),
                }
            );

            _selectedEmployee["picCode"] = picCode;
            _selectedEmployee["cropScale"] = ZoomSlider?.Value ?? 1.0;
            _selectedEmployee["cropX"] = EditorPhotoPreview?.TranslationX ?? 0.0;
            _selectedEmployee["cropY"] = EditorPhotoPreview?.TranslationY ?? 0.0;

            RenderBadgePreview(_selectedEmployee);
            if (PhotoEditorOverlay != null)
                PhotoEditorOverlay.IsVisible = false;
        }
        catch (Exception ex)
        {
            await DisplayAlert("Save Error", ex.Message, "OK");
        }
    }

/// <summary>
/// Hides the photo editor overlay when the user clicks the cancel button.
/// </summary>
/// <param name="sender">The object that invoked this method.</param>
/// <param name="e">The event data for this method.</param>
    private void OnCancelPhoto(object sender, EventArgs e)
    {
        if (PhotoEditorOverlay != null)
            PhotoEditorOverlay.IsVisible = false;
    }

        /// <summary>
        /// Safely converts an object to a double value. If the object is null or DBNull.Value, returns a default value.
        /// </summary>
        /// <param name="value">The object to convert.</param>
        /// <param name="defaultValue">The value to return if the object is null or DBNull.Value.</param>
        /// <returns>The double value of the object, or the default value if the object is null or DBNull.Value.</returns>
    private double SafeToDouble(object? value, double defaultValue = 1.0)
    {
        if (value == null || value == DBNull.Value)
            return defaultValue;
        return double.TryParse(value.ToString(), out double d) ? d : defaultValue;
    }

        /// <summary>
        /// Prints the selected employee's badge using the selected printer.
        /// </summary>
        /// <param name="sender">The object that invoked this method.</param>
        /// <param name="e">The event data for this method.</param>
    private async void OnPrintClicked(object sender, EventArgs e)
    {
        if (_selectedEmployee == null || PrinterPicker?.SelectedItem == null)
        {
            await DisplayAlert("Missing Information", "Select an employee and a printer.", "OK");
            return;
        }

        try
        {
            if (
                PreviewFrame.Content is HorizontalStackLayout mainLayout
                && mainLayout.Children.Count >= 2
            )
            {
                var frontView = mainLayout.Children[0] as VisualElement;
                var backView = mainLayout.Children[1] as VisualElement;

                if (frontView == null || backView == null)
                    return;

                var frontResult = await frontView.CaptureAsync();
                var backResult = await backView.CaptureAsync();

                string frontB64 = await StreamToBase64(frontResult);
                string backB64 = await StreamToBase64(backResult);

                _printerService.PrintBase64Badge(
                    frontB64,
                    backB64,
                    PrinterPicker.SelectedItem.ToString()!
                );

                await DisplayAlert("Success", "Badge sent to printer.", "OK");
            }
        }
        catch (Exception ex)
        {
            await DisplayAlert("Print Error", ex.Message, "OK");
        }
    }

/// <summary>
/// Converts a screenshot result stream to a base64 string.
/// </summary>
/// <param name="shot">The screenshot result stream to convert.</param>
/// <returns>The base64 string representation of the stream.</returns>
    private async Task<string> StreamToBase64(IScreenshotResult shot)
    {
        using var stream = await shot.OpenReadAsync();
        using var ms = new MemoryStream();
        await stream.CopyToAsync(ms);
        return Convert.ToBase64String(ms.ToArray());
    }
}
