using System.Diagnostics;
using System.Drawing;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;
using System.Windows.Forms;

namespace OpsDeskPro;

public sealed class MainForm : Form
{
    private readonly string _dataFolder;
    private readonly string _ticketFile;
    private readonly string _assetFile;
    private readonly string _reportFolder;

    private readonly List<TicketRecord> _tickets = new();
    private readonly List<AssetRecord> _assets = new();

    private AuditSnapshot? _latestAudit;
    private List<NetworkProbe> _latestNetworkChecks = new();

    private readonly TextBox _txtAuditSummary = new();
    private readonly DataGridView _dgvNetwork = new();
    private readonly Label _lblStatus = new();
    private readonly Label _lblTicketCount = new();
    private readonly Label _lblOpenTicketCount = new();
    private readonly Label _lblAssetCount = new();
    private readonly Label _lblReachableCount = new();

    private readonly TextBox _txtTicketSearch = new();
    private readonly TextBox _txtTicketTitle = new();
    private readonly ComboBox _cmbTicketCategory = new();
    private readonly ComboBox _cmbTicketPriority = new();
    private readonly ComboBox _cmbTicketStatus = new();
    private readonly TextBox _txtTicketOwner = new();
    private readonly TextBox _txtTicketNotes = new();
    private readonly DataGridView _dgvTickets = new();
    private string? _selectedTicketId;

    private readonly TextBox _txtAssetSearch = new();
    private readonly TextBox _txtAssetName = new();
    private readonly TextBox _txtAssetTag = new();
    private readonly TextBox _txtAssetLocation = new();
    private readonly TextBox _txtAssetAssignedTo = new();
    private readonly ComboBox _cmbAssetStatus = new();
    private readonly TextBox _txtAssetNotes = new();
    private readonly DataGridView _dgvAssets = new();
    private string? _selectedAssetId;

    public MainForm()
    {
        Text = "OpsDesk Career Suite";
        StartPosition = FormStartPosition.CenterScreen;
        MinimumSize = new Size(1280, 820);
        Size = new Size(1360, 900);
        Font = new Font("Segoe UI", 10F);

        _dataFolder = Path.Combine(AppContext.BaseDirectory, "data");
        _ticketFile = Path.Combine(_dataFolder, "tickets.json");
        _assetFile = Path.Combine(_dataFolder, "assets.json");
        _reportFolder = Path.Combine(_dataFolder, "reports");

        InitializeWindow();
        EnsureFolders();
        LoadData();
        BindNetworkGrid();
        RefreshAudit(false);
        RefreshDashboardCards();
    }

    private void InitializeWindow()
    {
        var root = new TableLayoutPanel
        {
            Dock = DockStyle.Fill,
            ColumnCount = 1,
            RowCount = 2,
            Padding = new Padding(14),
        };
        root.RowStyles.Add(new RowStyle(SizeType.AutoSize));
        root.RowStyles.Add(new RowStyle(SizeType.Percent, 100));

        var header = new Panel
        {
            Dock = DockStyle.Top,
            Height = 90,
            BackColor = Color.FromArgb(15, 23, 42),
            Padding = new Padding(18, 14, 18, 14),
            Margin = new Padding(0, 0, 0, 12),
        };

        var lblTitle = new Label
        {
            Text = "OpsDesk Career Suite",
            ForeColor = Color.White,
            Font = new Font("Segoe UI Semibold", 25F, FontStyle.Bold),
            Dock = DockStyle.Top,
            Height = 38,
        };

        var lblSubtitle = new Label
        {
            Text = "All-in-one Windows portfolio app for IT support, helpdesk, network checks, asset admin, reporting, and interview-ready demo workflows.",
            ForeColor = Color.Gainsboro,
            Dock = DockStyle.Bottom,
            Height = 26,
        };

        header.Controls.Add(lblSubtitle);
        header.Controls.Add(lblTitle);

        var tabs = new TabControl
        {
            Dock = DockStyle.Fill,
            ItemSize = new Size(180, 36),
            SizeMode = TabSizeMode.Fixed
        };

        tabs.TabPages.Add(BuildDashboardTab());
        tabs.TabPages.Add(BuildTicketsTab());
        tabs.TabPages.Add(BuildAssetsTab());
        tabs.TabPages.Add(BuildRoleMatchTab());
        tabs.TabPages.Add(BuildInterviewGuideTab());

        root.Controls.Add(header, 0, 0);
        root.Controls.Add(tabs, 0, 1);

        Controls.Add(root);
    }

    private TabPage BuildDashboardTab()
    {
        var page = new TabPage("Dashboard");

        var layout = new TableLayoutPanel
        {
            Dock = DockStyle.Fill,
            ColumnCount = 1,
            RowCount = 4,
            Padding = new Padding(12)
        };
        layout.RowStyles.Add(new RowStyle(SizeType.AutoSize));
        layout.RowStyles.Add(new RowStyle(SizeType.AutoSize));
        layout.RowStyles.Add(new RowStyle(SizeType.Percent, 56));
        layout.RowStyles.Add(new RowStyle(SizeType.Percent, 44));

        var actions = new FlowLayoutPanel
        {
            Dock = DockStyle.Fill,
            AutoSize = true,
            WrapContents = true,
            FlowDirection = FlowDirection.LeftToRight,
            Margin = new Padding(0, 0, 0, 10)
        };

        actions.Controls.Add(CreateActionButton("Refresh Audit", (_, _) => SafeRun("Refresh audit", () => RefreshAudit(true))));
        actions.Controls.Add(CreateActionButton("Run Network Checks", (_, _) => SafeRun("Network checks", RunNetworkChecks)));
        actions.Controls.Add(CreateActionButton("Seed Demo Data", (_, _) => SafeRun("Seed demo data", SeedDemoData)));
        actions.Controls.Add(CreateActionButton("Export Report", (_, _) => SafeRun("Export report", ExportReport)));
        actions.Controls.Add(CreateActionButton("Open Reports Folder", (_, _) => SafeRun("Open reports folder", () => OpenFolder(_reportFolder))));

        _lblStatus.Text = "Status: Ready";
        _lblStatus.AutoSize = true;
        _lblStatus.Margin = new Padding(18, 12, 0, 0);
        _lblStatus.Font = new Font("Segoe UI Semibold", 10F, FontStyle.Bold);
        actions.Controls.Add(_lblStatus);

        var cardWrap = new FlowLayoutPanel
        {
            Dock = DockStyle.Fill,
            AutoSize = true,
            WrapContents = true,
            FlowDirection = FlowDirection.LeftToRight,
            Margin = new Padding(0, 0, 0, 10)
        };

        cardWrap.Controls.Add(CreateMetricCard("Tickets", _lblTicketCount));
        cardWrap.Controls.Add(CreateMetricCard("Open / In Progress", _lblOpenTicketCount));
        cardWrap.Controls.Add(CreateMetricCard("Assets", _lblAssetCount));
        cardWrap.Controls.Add(CreateMetricCard("Reachable Hosts", _lblReachableCount));

        _txtAuditSummary.Multiline = true;
        _txtAuditSummary.Dock = DockStyle.Fill;
        _txtAuditSummary.ReadOnly = true;
        _txtAuditSummary.ScrollBars = ScrollBars.Vertical;
        _txtAuditSummary.BackColor = Color.White;
        _txtAuditSummary.Font = new Font("Consolas", 10F);

        _dgvNetwork.Dock = DockStyle.Fill;
        ConfigureReadOnlyGrid(_dgvNetwork);

        layout.Controls.Add(actions, 0, 0);
        layout.Controls.Add(cardWrap, 0, 1);
        layout.Controls.Add(_txtAuditSummary, 0, 2);
        layout.Controls.Add(_dgvNetwork, 0, 3);

        page.Controls.Add(layout);
        return page;
    }

    private TabPage BuildTicketsTab()
    {
        var page = new TabPage("Tickets");

        var split = new SplitContainer
        {
            Dock = DockStyle.Fill,
            Orientation = Orientation.Horizontal,
            SplitterDistance = 310,
            Padding = new Padding(12)
        };

        var upper = new TableLayoutPanel
        {
            Dock = DockStyle.Fill,
            ColumnCount = 1,
            RowCount = 3,
        };
        upper.RowStyles.Add(new RowStyle(SizeType.AutoSize));
        upper.RowStyles.Add(new RowStyle(SizeType.Percent, 100));
        upper.RowStyles.Add(new RowStyle(SizeType.AutoSize));

        var searchPanel = new Panel { Dock = DockStyle.Top, Height = 42 };
        var lblSearch = new Label
        {
            Text = "Search",
            AutoSize = true,
            Location = new Point(0, 11)
        };
        _txtTicketSearch.PlaceholderText = "Filter by title, owner, category, priority, or status";
        _txtTicketSearch.Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top;
        _txtTicketSearch.Location = new Point(72, 8);
        _txtTicketSearch.Width = 720;
        _txtTicketSearch.TextChanged += (_, _) => RefreshTicketGrid();
        searchPanel.Controls.Add(lblSearch);
        searchPanel.Controls.Add(_txtTicketSearch);
        searchPanel.Resize += (_, _) => _txtTicketSearch.Width = Math.Max(220, searchPanel.ClientSize.Width - 84);

        var form = new TableLayoutPanel
        {
            Dock = DockStyle.Fill,
            ColumnCount = 2,
            RowCount = 6,
        };
        form.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 20));
        form.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 80));

        AddLabeledInput(form, 0, "Title", _txtTicketTitle);
        AddLabeledInput(form, 1, "Category", ConfigureCombo(_cmbTicketCategory, "Support", "Network", "Software", "Access", "Hardware", "Database", "ERP", "Developer"));
        AddLabeledInput(form, 2, "Priority", ConfigureCombo(_cmbTicketPriority, "Low", "Medium", "High", "Critical"));
        AddLabeledInput(form, 3, "Status", ConfigureCombo(_cmbTicketStatus, "Open", "In Progress", "Resolved", "Closed"));
        AddLabeledInput(form, 4, "Owner", _txtTicketOwner);

        _txtTicketNotes.Multiline = true;
        _txtTicketNotes.Height = 72;
        AddLabeledInput(form, 5, "Notes", _txtTicketNotes);

        var ticketButtons = new FlowLayoutPanel
        {
            Dock = DockStyle.Bottom,
            AutoSize = true,
            Margin = new Padding(0, 10, 0, 0)
        };
        ticketButtons.Controls.Add(CreateActionButton("Add Ticket", (_, _) => SafeRun("Add ticket", AddTicket)));
        ticketButtons.Controls.Add(CreateActionButton("Update Ticket", (_, _) => SafeRun("Update ticket", UpdateTicket)));
        ticketButtons.Controls.Add(CreateActionButton("Mark Resolved", (_, _) => SafeRun("Resolve ticket", MarkTicketResolved)));
        ticketButtons.Controls.Add(CreateActionButton("Delete Ticket", (_, _) => SafeRun("Delete ticket", DeleteTicket)));
        ticketButtons.Controls.Add(CreateActionButton("Clear Form", (_, _) => ResetTicketInputs()));
        ticketButtons.Controls.Add(CreateActionButton("Refresh List", (_, _) => RefreshTicketGrid()));

        _dgvTickets.Dock = DockStyle.Fill;
        ConfigureReadOnlyGrid(_dgvTickets);
        _dgvTickets.CellDoubleClick += (_, e) =>
        {
            if (e.RowIndex >= 0)
            {
                LoadSelectedTicket();
            }
        };

        upper.Controls.Add(searchPanel, 0, 0);
        upper.Controls.Add(form, 0, 1);
        upper.Controls.Add(ticketButtons, 0, 2);

        split.Panel1.Controls.Add(upper);
        split.Panel2.Controls.Add(_dgvTickets);

        page.Controls.Add(split);
        return page;
    }

    private TabPage BuildAssetsTab()
    {
        var page = new TabPage("Assets");

        var split = new SplitContainer
        {
            Dock = DockStyle.Fill,
            Orientation = Orientation.Horizontal,
            SplitterDistance = 310,
            Padding = new Padding(12)
        };

        var upper = new TableLayoutPanel
        {
            Dock = DockStyle.Fill,
            ColumnCount = 1,
            RowCount = 3,
        };
        upper.RowStyles.Add(new RowStyle(SizeType.AutoSize));
        upper.RowStyles.Add(new RowStyle(SizeType.Percent, 100));
        upper.RowStyles.Add(new RowStyle(SizeType.AutoSize));

        var searchPanel = new Panel { Dock = DockStyle.Top, Height = 42 };
        var lblSearch = new Label
        {
            Text = "Search",
            AutoSize = true,
            Location = new Point(0, 11)
        };
        _txtAssetSearch.PlaceholderText = "Filter by device, asset tag, location, assignee, or status";
        _txtAssetSearch.Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top;
        _txtAssetSearch.Location = new Point(72, 8);
        _txtAssetSearch.Width = 720;
        _txtAssetSearch.TextChanged += (_, _) => RefreshAssetGrid();
        searchPanel.Controls.Add(lblSearch);
        searchPanel.Controls.Add(_txtAssetSearch);
        searchPanel.Resize += (_, _) => _txtAssetSearch.Width = Math.Max(220, searchPanel.ClientSize.Width - 84);

        var form = new TableLayoutPanel
        {
            Dock = DockStyle.Fill,
            ColumnCount = 2,
            RowCount = 6,
        };
        form.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 20));
        form.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 80));

        AddLabeledInput(form, 0, "Device Name", _txtAssetName);
        AddLabeledInput(form, 1, "Asset Tag", _txtAssetTag);
        AddLabeledInput(form, 2, "Location", _txtAssetLocation);
        AddLabeledInput(form, 3, "Assigned To", _txtAssetAssignedTo);
        AddLabeledInput(form, 4, "Status", ConfigureCombo(_cmbAssetStatus, "Active", "Spare", "Repair", "Retired"));

        _txtAssetNotes.Multiline = true;
        _txtAssetNotes.Height = 72;
        AddLabeledInput(form, 5, "Notes", _txtAssetNotes);

        var assetButtons = new FlowLayoutPanel
        {
            Dock = DockStyle.Bottom,
            AutoSize = true,
            Margin = new Padding(0, 10, 0, 0)
        };
        assetButtons.Controls.Add(CreateActionButton("Add Asset", (_, _) => SafeRun("Add asset", AddAsset)));
        assetButtons.Controls.Add(CreateActionButton("Update Asset", (_, _) => SafeRun("Update asset", UpdateAsset)));
        assetButtons.Controls.Add(CreateActionButton("Delete Asset", (_, _) => SafeRun("Delete asset", DeleteAsset)));
        assetButtons.Controls.Add(CreateActionButton("Clear Form", (_, _) => ResetAssetInputs()));
        assetButtons.Controls.Add(CreateActionButton("Refresh List", (_, _) => RefreshAssetGrid()));

        _dgvAssets.Dock = DockStyle.Fill;
        ConfigureReadOnlyGrid(_dgvAssets);
        _dgvAssets.CellDoubleClick += (_, e) =>
        {
            if (e.RowIndex >= 0)
            {
                LoadSelectedAsset();
            }
        };

        upper.Controls.Add(searchPanel, 0, 0);
        upper.Controls.Add(form, 0, 1);
        upper.Controls.Add(assetButtons, 0, 2);

        split.Panel1.Controls.Add(upper);
        split.Panel2.Controls.Add(_dgvAssets);

        page.Controls.Add(split);
        return page;
    }

    private TabPage BuildRoleMatchTab()
    {
        var page = new TabPage("Role Match");
        var box = new TextBox
        {
            Dock = DockStyle.Fill,
            Multiline = true,
            ReadOnly = true,
            ScrollBars = ScrollBars.Vertical,
            BackColor = Color.White,
            Font = new Font("Segoe UI", 10F),
            Text = BuildRoleMatchGuide(),
            Margin = new Padding(12)
        };

        var wrap = new Panel { Dock = DockStyle.Fill, Padding = new Padding(12) };
        wrap.Controls.Add(box);
        page.Controls.Add(wrap);
        return page;
    }

    private TabPage BuildInterviewGuideTab()
    {
        var page = new TabPage("Interview Guide");
        var box = new TextBox
        {
            Dock = DockStyle.Fill,
            Multiline = true,
            ReadOnly = true,
            ScrollBars = ScrollBars.Vertical,
            BackColor = Color.White,
            Font = new Font("Consolas", 10F),
            Text = BuildInterviewGuide(),
            Margin = new Padding(12)
        };

        var wrap = new Panel { Dock = DockStyle.Fill, Padding = new Padding(12) };
        wrap.Controls.Add(box);
        page.Controls.Add(wrap);
        return page;
    }

    private void RefreshAudit(bool showMessage)
    {
        _latestAudit = BuildAuditSnapshot();
        _txtAuditSummary.Text = BuildAuditSummary(_latestAudit);
        _lblStatus.Text = $"Status: {_latestAudit.Health}";
        RefreshDashboardCards();

        if (showMessage)
        {
            MessageBox.Show(this, "Audit refreshed successfully.", "OpsDesk Career Suite", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
    }

    private void BindNetworkGrid()
    {
        _dgvNetwork.DataSource = _latestNetworkChecks
            .Select(x => new
            {
                x.Host,
                x.Reachable,
                x.RoundtripMs,
                x.Notes,
                CheckedAt = x.CheckedAt.ToString("yyyy-MM-dd HH:mm:ss")
            })
            .ToList();

        RefreshDashboardCards();
    }

    private AuditSnapshot BuildAuditSnapshot()
    {
        var systemDrive = GetSystemDriveInfo();
        var boot = TimeSpan.FromMilliseconds(Environment.TickCount64);

        return new AuditSnapshot
        {
            CapturedAt = DateTime.Now,
            MachineName = Environment.MachineName,
            UserName = Environment.UserName,
            DomainName = Environment.UserDomainName,
            OsDescription = RuntimeInformation.OSDescription,
            Framework = RuntimeInformation.FrameworkDescription,
            ProcessArchitecture = RuntimeInformation.ProcessArchitecture.ToString(),
            Is64Bit = Environment.Is64BitOperatingSystem,
            ProcessorCount = Environment.ProcessorCount,
            Uptime = boot,
            CurrentDirectory = Environment.CurrentDirectory,
            SystemDirectory = Environment.SystemDirectory,
            SystemDrive = systemDrive.RootPath,
            SystemDriveFreeGb = systemDrive.FreeGb,
            SystemDriveTotalGb = systemDrive.TotalGb,
            LocalIpv4 = GetLocalIpv4Addresses(),
            Health = GetHealthLabel(systemDrive),
            Notes = GetHealthNotes(systemDrive)
        };
    }

    private List<NetworkProbe> BuildNetworkChecks()
    {
        var targets = new[] { "localhost", "1.1.1.1", "8.8.8.8", "google.com", "github.com" };
        var checks = new List<NetworkProbe>();

        foreach (var target in targets)
        {
            checks.Add(RunPing(target));
        }

        return checks;
    }

    private NetworkProbe RunPing(string host)
    {
        try
        {
            using var ping = new Ping();
            var reply = ping.Send(host, 1200);

            return new NetworkProbe
            {
                Host = host,
                CheckedAt = DateTime.Now,
                Reachable = reply is not null && reply.Status == IPStatus.Success,
                RoundtripMs = reply is not null && reply.Status == IPStatus.Success ? reply.RoundtripTime : -1,
                Notes = reply?.Status.ToString() ?? "No response"
            };
        }
        catch (Exception ex)
        {
            return new NetworkProbe
            {
                Host = host,
                CheckedAt = DateTime.Now,
                Reachable = false,
                RoundtripMs = -1,
                Notes = ex.Message
            };
        }
    }

    private string BuildAuditSummary(AuditSnapshot audit)
    {
        var uptime = $"{(int)audit.Uptime.TotalDays}d {audit.Uptime.Hours}h {audit.Uptime.Minutes}m";
        var builder = new StringBuilder();

        builder.AppendLine("OPS DESK CAREER SUITE :: LIVE AUDIT");
        builder.AppendLine(new string('=', 70));
        builder.AppendLine($"Captured At      : {audit.CapturedAt:yyyy-MM-dd HH:mm:ss}");
        builder.AppendLine($"Machine Name     : {audit.MachineName}");
        builder.AppendLine($"User / Domain    : {audit.UserName} / {audit.DomainName}");
        builder.AppendLine($"OS               : {audit.OsDescription}");
        builder.AppendLine($"Runtime          : {audit.Framework}");
        builder.AppendLine($"Architecture     : {audit.ProcessArchitecture} | 64-bit OS = {audit.Is64Bit}");
        builder.AppendLine($"CPU Cores        : {audit.ProcessorCount}");
        builder.AppendLine($"Uptime           : {uptime}");
        builder.AppendLine($"Current Dir      : {audit.CurrentDirectory}");
        builder.AppendLine($"System Dir       : {audit.SystemDirectory}");
        builder.AppendLine($"System Drive     : {audit.SystemDrive} | Free {audit.SystemDriveFreeGb:N2} GB / {audit.SystemDriveTotalGb:N2} GB");
        builder.AppendLine($"IPv4 Addresses   : {(audit.LocalIpv4.Count == 0 ? "Not found" : string.Join(", ", audit.LocalIpv4))}");
        builder.AppendLine($"Health           : {audit.Health}");
        builder.AppendLine($"Notes            : {audit.Notes}");
        builder.AppendLine();
        builder.AppendLine("HOW TO EXPLAIN THIS IN INTERVIEW:");
        builder.AppendLine("- I use this dashboard to prove that I can inspect endpoints, read OS/runtime context, and spot basic health risks quickly.");
        builder.AppendLine("- This simulates first-line troubleshooting before escalation or deeper server-side analysis.");

        return builder.ToString();
    }

    private void RunNetworkChecks()
    {
        _latestNetworkChecks = BuildNetworkChecks();
        BindNetworkGrid();

        var reachableCount = _latestNetworkChecks.Count(x => x.Reachable);
        MessageBox.Show(this,
            $"Network checks completed.\nReachable targets: {reachableCount}/{_latestNetworkChecks.Count}",
            "OpsDesk Career Suite",
            MessageBoxButtons.OK,
            MessageBoxIcon.Information);
    }

    private void SeedDemoData()
    {
        if (_tickets.Count == 0)
        {
            _tickets.AddRange(new[]
            {
                new TicketRecord
                {
                    Id = NextTicketId(),
                    CreatedAt = DateTime.Now.AddMinutes(-45),
                    UpdatedAt = DateTime.Now.AddMinutes(-20),
                    Title = "VPN user cannot connect to office network",
                    Category = "Network",
                    Priority = "High",
                    Owner = Environment.UserName,
                    Status = "In Progress",
                    Notes = "Checked IP reachability, confirmed DNS works, next step is adapter reset and credential validation."
                },
                new TicketRecord
                {
                    Id = NextTicketId(),
                    CreatedAt = DateTime.Now.AddMinutes(-18),
                    UpdatedAt = DateTime.Now.AddMinutes(-8),
                    Title = "Printer service not responding on shared workstation",
                    Category = "Hardware",
                    Priority = "Medium",
                    Owner = Environment.UserName,
                    Status = "Open",
                    Notes = "User reported print queue stuck. Need spooler restart and local driver check."
                }
            });
            SaveJson(_ticketFile, _tickets);
        }

        if (_assets.Count == 0)
        {
            _assets.AddRange(new[]
            {
                new AssetRecord
                {
                    Id = NextAssetId(),
                    SavedAt = DateTime.Now.AddDays(-5),
                    UpdatedAt = DateTime.Now.AddDays(-1),
                    DeviceName = "Laptop-IT-01",
                    AssetTag = "AST-LTP-001",
                    Location = "Jakarta HQ",
                    AssignedTo = "Service Desk",
                    Status = "Active",
                    Notes = "Primary support laptop for daily ticket handling and remote diagnostics."
                },
                new AssetRecord
                {
                    Id = NextAssetId(),
                    SavedAt = DateTime.Now.AddDays(-20),
                    UpdatedAt = DateTime.Now.AddDays(-2),
                    DeviceName = "Switch-3F-East",
                    AssetTag = "AST-NET-014",
                    Location = "Server Room Rack B",
                    AssignedTo = "Network Team",
                    Status = "Repair",
                    Notes = "Intermittent port issue reported. Marked for inspection and possible replacement."
                }
            });
            SaveJson(_assetFile, _assets);
        }

        RefreshTicketGrid();
        RefreshAssetGrid();
        RefreshDashboardCards();

        MessageBox.Show(this,
            "Demo data loaded. You can now present realistic ticket and asset scenarios during interviews.",
            "OpsDesk Career Suite",
            MessageBoxButtons.OK,
            MessageBoxIcon.Information);
    }

    private void AddTicket()
    {
        var title = _txtTicketTitle.Text.Trim();
        if (string.IsNullOrWhiteSpace(title))
        {
            MessageBox.Show(this, "Ticket title is required.", "Validation", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            _txtTicketTitle.Focus();
            return;
        }

        var ticket = new TicketRecord
        {
            Id = NextTicketId(),
            CreatedAt = DateTime.Now,
            UpdatedAt = DateTime.Now,
            Title = title,
            Category = SafeComboValue(_cmbTicketCategory, "Support"),
            Priority = SafeComboValue(_cmbTicketPriority, "Medium"),
            Owner = string.IsNullOrWhiteSpace(_txtTicketOwner.Text) ? Environment.UserName : _txtTicketOwner.Text.Trim(),
            Status = SafeComboValue(_cmbTicketStatus, "Open"),
            Notes = _txtTicketNotes.Text.Trim()
        };

        _tickets.Insert(0, ticket);
        SaveJson(_ticketFile, _tickets);
        RefreshTicketGrid();
        ResetTicketInputs();

        MessageBox.Show(this, $"Ticket {ticket.Id} saved.", "OpsDesk Career Suite", MessageBoxButtons.OK, MessageBoxIcon.Information);
    }

    private void UpdateTicket()
    {
        var ticket = GetSelectedTicketOrNull();
        if (ticket is null)
        {
            MessageBox.Show(this, "Double-click a ticket row first to load it into the form.", "Update Ticket", MessageBoxButtons.OK, MessageBoxIcon.Information);
            return;
        }

        var title = _txtTicketTitle.Text.Trim();
        if (string.IsNullOrWhiteSpace(title))
        {
            MessageBox.Show(this, "Ticket title is required.", "Validation", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            _txtTicketTitle.Focus();
            return;
        }

        ticket.Title = title;
        ticket.Category = SafeComboValue(_cmbTicketCategory, ticket.Category);
        ticket.Priority = SafeComboValue(_cmbTicketPriority, ticket.Priority);
        ticket.Status = SafeComboValue(_cmbTicketStatus, ticket.Status);
        ticket.Owner = string.IsNullOrWhiteSpace(_txtTicketOwner.Text) ? Environment.UserName : _txtTicketOwner.Text.Trim();
        ticket.Notes = _txtTicketNotes.Text.Trim();
        ticket.UpdatedAt = DateTime.Now;

        SaveJson(_ticketFile, _tickets);
        RefreshTicketGrid();

        MessageBox.Show(this, $"Ticket {ticket.Id} updated.", "OpsDesk Career Suite", MessageBoxButtons.OK, MessageBoxIcon.Information);
    }

    private void MarkTicketResolved()
    {
        var ticket = GetSelectedTicketOrNull();
        if (ticket is null)
        {
            MessageBox.Show(this, "Double-click a ticket row first to load it into the form.", "Resolve Ticket", MessageBoxButtons.OK, MessageBoxIcon.Information);
            return;
        }

        ticket.Status = "Resolved";
        ticket.UpdatedAt = DateTime.Now;
        SaveJson(_ticketFile, _tickets);
        RefreshTicketGrid();
        LoadTicketToForm(ticket);

        MessageBox.Show(this, $"Ticket {ticket.Id} marked as Resolved.", "OpsDesk Career Suite", MessageBoxButtons.OK, MessageBoxIcon.Information);
    }

    private void DeleteTicket()
    {
        var ticket = GetSelectedTicketOrNull();
        if (ticket is null)
        {
            MessageBox.Show(this, "Double-click a ticket row first to load it into the form.", "Delete Ticket", MessageBoxButtons.OK, MessageBoxIcon.Information);
            return;
        }

        var confirm = MessageBox.Show(this,
            $"Delete ticket {ticket.Id}?\nThis removes the record from local JSON storage.",
            "Confirm Delete",
            MessageBoxButtons.YesNo,
            MessageBoxIcon.Warning);

        if (confirm != DialogResult.Yes)
        {
            return;
        }

        _tickets.Remove(ticket);
        SaveJson(_ticketFile, _tickets);
        RefreshTicketGrid();
        ResetTicketInputs();

        MessageBox.Show(this, "Ticket deleted.", "OpsDesk Career Suite", MessageBoxButtons.OK, MessageBoxIcon.Information);
    }

    private void AddAsset()
    {
        var deviceName = _txtAssetName.Text.Trim();
        if (string.IsNullOrWhiteSpace(deviceName))
        {
            MessageBox.Show(this, "Device name is required.", "Validation", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            _txtAssetName.Focus();
            return;
        }

        var asset = new AssetRecord
        {
            Id = NextAssetId(),
            SavedAt = DateTime.Now,
            UpdatedAt = DateTime.Now,
            DeviceName = deviceName,
            AssetTag = _txtAssetTag.Text.Trim(),
            Location = _txtAssetLocation.Text.Trim(),
            AssignedTo = _txtAssetAssignedTo.Text.Trim(),
            Status = SafeComboValue(_cmbAssetStatus, "Active"),
            Notes = _txtAssetNotes.Text.Trim()
        };

        _assets.Insert(0, asset);
        SaveJson(_assetFile, _assets);
        RefreshAssetGrid();
        ResetAssetInputs();

        MessageBox.Show(this, $"Asset {asset.Id} saved.", "OpsDesk Career Suite", MessageBoxButtons.OK, MessageBoxIcon.Information);
    }

    private void UpdateAsset()
    {
        var asset = GetSelectedAssetOrNull();
        if (asset is null)
        {
            MessageBox.Show(this, "Double-click an asset row first to load it into the form.", "Update Asset", MessageBoxButtons.OK, MessageBoxIcon.Information);
            return;
        }

        var deviceName = _txtAssetName.Text.Trim();
        if (string.IsNullOrWhiteSpace(deviceName))
        {
            MessageBox.Show(this, "Device name is required.", "Validation", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            _txtAssetName.Focus();
            return;
        }

        asset.DeviceName = deviceName;
        asset.AssetTag = _txtAssetTag.Text.Trim();
        asset.Location = _txtAssetLocation.Text.Trim();
        asset.AssignedTo = _txtAssetAssignedTo.Text.Trim();
        asset.Status = SafeComboValue(_cmbAssetStatus, asset.Status);
        asset.Notes = _txtAssetNotes.Text.Trim();
        asset.UpdatedAt = DateTime.Now;

        SaveJson(_assetFile, _assets);
        RefreshAssetGrid();

        MessageBox.Show(this, $"Asset {asset.Id} updated.", "OpsDesk Career Suite", MessageBoxButtons.OK, MessageBoxIcon.Information);
    }

    private void DeleteAsset()
    {
        var asset = GetSelectedAssetOrNull();
        if (asset is null)
        {
            MessageBox.Show(this, "Double-click an asset row first to load it into the form.", "Delete Asset", MessageBoxButtons.OK, MessageBoxIcon.Information);
            return;
        }

        var confirm = MessageBox.Show(this,
            $"Delete asset {asset.Id}?\nThis removes the record from local JSON storage.",
            "Confirm Delete",
            MessageBoxButtons.YesNo,
            MessageBoxIcon.Warning);

        if (confirm != DialogResult.Yes)
        {
            return;
        }

        _assets.Remove(asset);
        SaveJson(_assetFile, _assets);
        RefreshAssetGrid();
        ResetAssetInputs();

        MessageBox.Show(this, "Asset deleted.", "OpsDesk Career Suite", MessageBoxButtons.OK, MessageBoxIcon.Information);
    }

    private void ExportReport()
    {
        _latestAudit ??= BuildAuditSnapshot();
        _latestNetworkChecks = _latestNetworkChecks.Count == 0 ? BuildNetworkChecks() : _latestNetworkChecks;

        var stamp = DateTime.Now.ToString("yyyyMMdd-HHmmss");
        var jsonPath = Path.Combine(_reportFolder, $"audit-{stamp}.json");
        var ticketCsv = Path.Combine(_reportFolder, $"tickets-{stamp}.csv");
        var assetCsv = Path.Combine(_reportFolder, $"assets-{stamp}.csv");
        var htmlPath = Path.Combine(_reportFolder, $"dashboard-{stamp}.html");

        var exportBundle = new ExportBundle
        {
            Audit = _latestAudit,
            Network = _latestNetworkChecks,
            Tickets = _tickets,
            Assets = _assets,
            Summary = BuildExportSummary()
        };

        SaveJson(jsonPath, exportBundle);
        File.WriteAllText(ticketCsv, BuildTicketCsv(), Encoding.UTF8);
        File.WriteAllText(assetCsv, BuildAssetCsv(), Encoding.UTF8);
        File.WriteAllText(htmlPath, BuildHtmlReport(), Encoding.UTF8);

        MessageBox.Show(this,
            "Report exported successfully.\n\n" +
            $"JSON: {Path.GetFileName(jsonPath)}\n" +
            $"Tickets CSV: {Path.GetFileName(ticketCsv)}\n" +
            $"Assets CSV: {Path.GetFileName(assetCsv)}\n" +
            $"HTML: {Path.GetFileName(htmlPath)}",
            "OpsDesk Career Suite",
            MessageBoxButtons.OK,
            MessageBoxIcon.Information);
    }

    private ExportSummary BuildExportSummary()
    {
        return new ExportSummary
        {
            TicketCount = _tickets.Count,
            OpenTicketCount = _tickets.Count(x => x.Status is "Open" or "In Progress"),
            AssetCount = _assets.Count,
            ReachableHostCount = _latestNetworkChecks.Count(x => x.Reachable),
            TotalHostCount = _latestNetworkChecks.Count,
            GeneratedAt = DateTime.Now,
            PortfolioPitch = "This app demonstrates endpoint checks, network diagnostics, helpdesk documentation, asset administration, and operational reporting."
        };
    }

    private string BuildTicketCsv()
    {
        var sb = new StringBuilder();
        sb.AppendLine("Id,CreatedAt,UpdatedAt,Title,Category,Priority,Owner,Status,Notes");
        foreach (var ticket in _tickets)
        {
            sb.AppendLine(string.Join(",",
                Csv(ticket.Id),
                Csv(ticket.CreatedAt.ToString("yyyy-MM-dd HH:mm:ss")),
                Csv(ticket.UpdatedAt.ToString("yyyy-MM-dd HH:mm:ss")),
                Csv(ticket.Title),
                Csv(ticket.Category),
                Csv(ticket.Priority),
                Csv(ticket.Owner),
                Csv(ticket.Status),
                Csv(ticket.Notes)));
        }

        return sb.ToString();
    }

    private string BuildAssetCsv()
    {
        var sb = new StringBuilder();
        sb.AppendLine("Id,SavedAt,UpdatedAt,DeviceName,AssetTag,Location,AssignedTo,Status,Notes");
        foreach (var asset in _assets)
        {
            sb.AppendLine(string.Join(",",
                Csv(asset.Id),
                Csv(asset.SavedAt.ToString("yyyy-MM-dd HH:mm:ss")),
                Csv(asset.UpdatedAt.ToString("yyyy-MM-dd HH:mm:ss")),
                Csv(asset.DeviceName),
                Csv(asset.AssetTag),
                Csv(asset.Location),
                Csv(asset.AssignedTo),
                Csv(asset.Status),
                Csv(asset.Notes)));
        }

        return sb.ToString();
    }

    private string BuildHtmlReport()
    {
        _latestAudit ??= BuildAuditSnapshot();
        var summary = BuildExportSummary();

        var auditSummary = new Dictionary<string, string>
        {
            ["Captured At"] = _latestAudit.CapturedAt.ToString("yyyy-MM-dd HH:mm:ss"),
            ["Machine Name"] = _latestAudit.MachineName,
            ["User"] = _latestAudit.UserName,
            ["Domain"] = _latestAudit.DomainName,
            ["OS"] = _latestAudit.OsDescription,
            ["Runtime"] = _latestAudit.Framework,
            ["Architecture"] = _latestAudit.ProcessArchitecture,
            ["64-bit OS"] = _latestAudit.Is64Bit.ToString(),
            ["CPU Cores"] = _latestAudit.ProcessorCount.ToString(),
            ["System Drive"] = _latestAudit.SystemDrive,
            ["Free Space (GB)"] = _latestAudit.SystemDriveFreeGb.ToString("N2"),
            ["Total Space (GB)"] = _latestAudit.SystemDriveTotalGb.ToString("N2"),
            ["Health"] = _latestAudit.Health,
            ["Notes"] = _latestAudit.Notes
        };

        var sb = new StringBuilder();
        sb.AppendLine("<!doctype html>");
        sb.AppendLine("<html lang=\"en\"><head><meta charset=\"utf-8\">");
        sb.AppendLine("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
        sb.AppendLine("<title>OpsDesk Career Suite Report</title>");
        sb.AppendLine("<style>");
        sb.AppendLine("body{font-family:Segoe UI,Arial,sans-serif;background:#f8fafc;color:#0f172a;margin:0;padding:24px;}");
        sb.AppendLine(".wrap{max-width:1150px;margin:auto;}");
        sb.AppendLine(".hero{background:linear-gradient(135deg,#0f172a,#2563eb);color:#fff;padding:24px;border-radius:20px;box-shadow:0 10px 30px rgba(15,23,42,.18);}");
        sb.AppendLine(".grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:14px;margin:18px 0;}");
        sb.AppendLine(".card{background:#fff;border-radius:18px;padding:18px;box-shadow:0 8px 24px rgba(15,23,42,.08);}");
        sb.AppendLine("table{width:100%;border-collapse:collapse;background:#fff;border-radius:18px;overflow:hidden;box-shadow:0 8px 24px rgba(15,23,42,.08);}");
        sb.AppendLine("th,td{padding:12px;border-bottom:1px solid #e2e8f0;text-align:left;}");
        sb.AppendLine("th{background:#eff6ff;}");
        sb.AppendLine("h2{margin-top:28px;}");
        sb.AppendLine("</style></head><body><div class=\"wrap\">");
        sb.AppendLine($"<section class=\"hero\"><h1>OpsDesk Career Suite Report</h1><p>Generated {WebUtility.HtmlEncode(summary.GeneratedAt.ToString("yyyy-MM-dd HH:mm:ss"))}</p><p>{WebUtility.HtmlEncode(summary.PortfolioPitch)}</p></section>");

        sb.AppendLine("<div class=\"grid\">");
        sb.AppendLine($"<div class=\"card\"><strong>Tickets</strong><br>{summary.TicketCount}</div>");
        sb.AppendLine($"<div class=\"card\"><strong>Open / In Progress</strong><br>{summary.OpenTicketCount}</div>");
        sb.AppendLine($"<div class=\"card\"><strong>Assets</strong><br>{summary.AssetCount}</div>");
        sb.AppendLine($"<div class=\"card\"><strong>Reachable Hosts</strong><br>{summary.ReachableHostCount}/{summary.TotalHostCount}</div>");
        sb.AppendLine("</div>");

        sb.AppendLine("<div class=\"grid\">");
        foreach (var item in auditSummary)
        {
            sb.AppendLine("<div class=\"card\">");
            sb.AppendLine($"<strong>{WebUtility.HtmlEncode(item.Key)}</strong><br>");
            sb.AppendLine($"{WebUtility.HtmlEncode(item.Value)}");
            sb.AppendLine("</div>");
        }
        sb.AppendLine("</div>");

        sb.AppendLine("<h2>Network Probes</h2>");
        sb.AppendLine("<table><thead><tr><th>Host</th><th>Reachable</th><th>Roundtrip (ms)</th><th>Notes</th><th>Checked At</th></tr></thead><tbody>");
        foreach (var row in _latestNetworkChecks)
        {
            sb.AppendLine("<tr>");
            sb.AppendLine($"<td>{WebUtility.HtmlEncode(row.Host)}</td>");
            sb.AppendLine($"<td>{WebUtility.HtmlEncode(row.Reachable.ToString())}</td>");
            sb.AppendLine($"<td>{WebUtility.HtmlEncode(row.RoundtripMs.ToString())}</td>");
            sb.AppendLine($"<td>{WebUtility.HtmlEncode(row.Notes)}</td>");
            sb.AppendLine($"<td>{WebUtility.HtmlEncode(row.CheckedAt.ToString("yyyy-MM-dd HH:mm:ss"))}</td>");
            sb.AppendLine("</tr>");
        }
        if (_latestNetworkChecks.Count == 0)
        {
            sb.AppendLine("<tr><td colspan=\"5\">No network checks have been run yet.</td></tr>");
        }
        sb.AppendLine("</tbody></table>");

        sb.AppendLine("<h2>Tickets</h2>");
        sb.AppendLine("<table><thead><tr><th>ID</th><th>Title</th><th>Category</th><th>Priority</th><th>Owner</th><th>Status</th></tr></thead><tbody>");
        foreach (var ticket in _tickets)
        {
            sb.AppendLine("<tr>");
            sb.AppendLine($"<td>{WebUtility.HtmlEncode(ticket.Id)}</td>");
            sb.AppendLine($"<td>{WebUtility.HtmlEncode(ticket.Title)}</td>");
            sb.AppendLine($"<td>{WebUtility.HtmlEncode(ticket.Category)}</td>");
            sb.AppendLine($"<td>{WebUtility.HtmlEncode(ticket.Priority)}</td>");
            sb.AppendLine($"<td>{WebUtility.HtmlEncode(ticket.Owner)}</td>");
            sb.AppendLine($"<td>{WebUtility.HtmlEncode(ticket.Status)}</td>");
            sb.AppendLine("</tr>");
        }
        if (_tickets.Count == 0)
        {
            sb.AppendLine("<tr><td colspan=\"6\">No tickets logged yet.</td></tr>");
        }
        sb.AppendLine("</tbody></table>");

        sb.AppendLine("<h2>Assets</h2>");
        sb.AppendLine("<table><thead><tr><th>ID</th><th>Device</th><th>Asset Tag</th><th>Location</th><th>Assigned To</th><th>Status</th></tr></thead><tbody>");
        foreach (var asset in _assets)
        {
            sb.AppendLine("<tr>");
            sb.AppendLine($"<td>{WebUtility.HtmlEncode(asset.Id)}</td>");
            sb.AppendLine($"<td>{WebUtility.HtmlEncode(asset.DeviceName)}</td>");
            sb.AppendLine($"<td>{WebUtility.HtmlEncode(asset.AssetTag)}</td>");
            sb.AppendLine($"<td>{WebUtility.HtmlEncode(asset.Location)}</td>");
            sb.AppendLine($"<td>{WebUtility.HtmlEncode(asset.AssignedTo)}</td>");
            sb.AppendLine($"<td>{WebUtility.HtmlEncode(asset.Status)}</td>");
            sb.AppendLine("</tr>");
        }
        if (_assets.Count == 0)
        {
            sb.AppendLine("<tr><td colspan=\"6\">No assets saved yet.</td></tr>");
        }
        sb.AppendLine("</tbody></table>");

        sb.AppendLine("</div></body></html>");
        return sb.ToString();
    }

    private void RefreshTicketGrid()
    {
        var search = _txtTicketSearch.Text.Trim();
        IEnumerable<TicketRecord> query = _tickets;

        if (!string.IsNullOrWhiteSpace(search))
        {
            query = query.Where(t => ContainsIgnoreCase(t.Id, search)
                || ContainsIgnoreCase(t.Title, search)
                || ContainsIgnoreCase(t.Category, search)
                || ContainsIgnoreCase(t.Priority, search)
                || ContainsIgnoreCase(t.Owner, search)
                || ContainsIgnoreCase(t.Status, search)
                || ContainsIgnoreCase(t.Notes, search));
        }

        _dgvTickets.DataSource = query
            .OrderByDescending(t => t.CreatedAt)
            .Select(t => new
            {
                t.Id,
                CreatedAt = t.CreatedAt.ToString("yyyy-MM-dd HH:mm:ss"),
                UpdatedAt = t.UpdatedAt.ToString("yyyy-MM-dd HH:mm:ss"),
                t.Title,
                t.Category,
                t.Priority,
                t.Owner,
                t.Status,
                t.Notes
            })
            .ToList();

        RefreshDashboardCards();
    }

    private void RefreshAssetGrid()
    {
        var search = _txtAssetSearch.Text.Trim();
        IEnumerable<AssetRecord> query = _assets;

        if (!string.IsNullOrWhiteSpace(search))
        {
            query = query.Where(a => ContainsIgnoreCase(a.Id, search)
                || ContainsIgnoreCase(a.DeviceName, search)
                || ContainsIgnoreCase(a.AssetTag, search)
                || ContainsIgnoreCase(a.Location, search)
                || ContainsIgnoreCase(a.AssignedTo, search)
                || ContainsIgnoreCase(a.Status, search)
                || ContainsIgnoreCase(a.Notes, search));
        }

        _dgvAssets.DataSource = query
            .OrderByDescending(a => a.UpdatedAt)
            .Select(a => new
            {
                a.Id,
                SavedAt = a.SavedAt.ToString("yyyy-MM-dd HH:mm:ss"),
                UpdatedAt = a.UpdatedAt.ToString("yyyy-MM-dd HH:mm:ss"),
                a.DeviceName,
                a.AssetTag,
                a.Location,
                a.AssignedTo,
                a.Status,
                a.Notes
            })
            .ToList();

        RefreshDashboardCards();
    }

    private void LoadSelectedTicket()
    {
        var id = GetGridCellString(_dgvTickets, "Id");
        if (string.IsNullOrWhiteSpace(id))
        {
            return;
        }

        var ticket = _tickets.FirstOrDefault(x => x.Id == id);
        if (ticket is null)
        {
            return;
        }

        LoadTicketToForm(ticket);
    }

    private void LoadTicketToForm(TicketRecord ticket)
    {
        _selectedTicketId = ticket.Id;
        _txtTicketTitle.Text = ticket.Title;
        SelectComboValue(_cmbTicketCategory, ticket.Category);
        SelectComboValue(_cmbTicketPriority, ticket.Priority);
        SelectComboValue(_cmbTicketStatus, ticket.Status);
        _txtTicketOwner.Text = ticket.Owner;
        _txtTicketNotes.Text = ticket.Notes;
        _txtTicketTitle.Focus();
    }

    private void LoadSelectedAsset()
    {
        var id = GetGridCellString(_dgvAssets, "Id");
        if (string.IsNullOrWhiteSpace(id))
        {
            return;
        }

        var asset = _assets.FirstOrDefault(x => x.Id == id);
        if (asset is null)
        {
            return;
        }

        LoadAssetToForm(asset);
    }

    private void LoadAssetToForm(AssetRecord asset)
    {
        _selectedAssetId = asset.Id;
        _txtAssetName.Text = asset.DeviceName;
        _txtAssetTag.Text = asset.AssetTag;
        _txtAssetLocation.Text = asset.Location;
        _txtAssetAssignedTo.Text = asset.AssignedTo;
        SelectComboValue(_cmbAssetStatus, asset.Status);
        _txtAssetNotes.Text = asset.Notes;
        _txtAssetName.Focus();
    }

    private TicketRecord? GetSelectedTicketOrNull()
        => string.IsNullOrWhiteSpace(_selectedTicketId)
            ? null
            : _tickets.FirstOrDefault(x => x.Id == _selectedTicketId);

    private AssetRecord? GetSelectedAssetOrNull()
        => string.IsNullOrWhiteSpace(_selectedAssetId)
            ? null
            : _assets.FirstOrDefault(x => x.Id == _selectedAssetId);

    private void RefreshDashboardCards()
    {
        _lblTicketCount.Text = _tickets.Count.ToString();
        _lblOpenTicketCount.Text = _tickets.Count(x => x.Status is "Open" or "In Progress").ToString();
        _lblAssetCount.Text = _assets.Count.ToString();
        _lblReachableCount.Text = $"{_latestNetworkChecks.Count(x => x.Reachable)}/{_latestNetworkChecks.Count}";
    }

    private void EnsureFolders()
    {
        Directory.CreateDirectory(_dataFolder);
        Directory.CreateDirectory(_reportFolder);
    }

    private void LoadData()
    {
        _tickets.Clear();
        _tickets.AddRange(LoadJson<List<TicketRecord>>(_ticketFile) ?? new List<TicketRecord>());
        foreach (var ticket in _tickets)
        {
            if (ticket.CreatedAt == default) ticket.CreatedAt = DateTime.Now;
            if (ticket.UpdatedAt == default) ticket.UpdatedAt = ticket.CreatedAt;
            if (string.IsNullOrWhiteSpace(ticket.Status)) ticket.Status = "Open";
        }

        _assets.Clear();
        _assets.AddRange(LoadJson<List<AssetRecord>>(_assetFile) ?? new List<AssetRecord>());
        foreach (var asset in _assets)
        {
            if (asset.SavedAt == default) asset.SavedAt = DateTime.Now;
            if (asset.UpdatedAt == default) asset.UpdatedAt = asset.SavedAt;
            if (string.IsNullOrWhiteSpace(asset.Status)) asset.Status = "Active";
        }

        RefreshTicketGrid();
        RefreshAssetGrid();

        if (_cmbTicketCategory.Items.Count > 0) _cmbTicketCategory.SelectedIndex = 0;
        if (_cmbTicketPriority.Items.Count > 1) _cmbTicketPriority.SelectedIndex = 1;
        else if (_cmbTicketPriority.Items.Count > 0) _cmbTicketPriority.SelectedIndex = 0;
        if (_cmbTicketStatus.Items.Count > 0) _cmbTicketStatus.SelectedIndex = 0;
        if (_cmbAssetStatus.Items.Count > 0) _cmbAssetStatus.SelectedIndex = 0;
        _txtTicketOwner.Text = Environment.UserName;
    }

    private T? LoadJson<T>(string filePath)
    {
        try
        {
            if (!File.Exists(filePath))
            {
                return default;
            }

            var json = File.ReadAllText(filePath, Encoding.UTF8);
            if (string.IsNullOrWhiteSpace(json))
            {
                return default;
            }

            return JsonSerializer.Deserialize<T>(json, new JsonSerializerOptions
            {
                PropertyNameCaseInsensitive = true
            });
        }
        catch
        {
            return default;
        }
    }

    private void SaveJson<T>(string filePath, T value)
    {
        Directory.CreateDirectory(Path.GetDirectoryName(filePath) ?? _dataFolder);
        var options = new JsonSerializerOptions { WriteIndented = true };
        var json = JsonSerializer.Serialize(value, options);
        File.WriteAllText(filePath, json, Encoding.UTF8);
    }

    private Button CreateActionButton(string text, EventHandler onClick)
    {
        var button = new Button
        {
            Text = text,
            AutoSize = true,
            MinimumSize = new Size(168, 38),
            BackColor = Color.FromArgb(37, 99, 235),
            ForeColor = Color.White,
            FlatStyle = FlatStyle.Flat,
            Margin = new Padding(0, 0, 10, 10),
            Padding = new Padding(10, 6, 10, 6)
        };
        button.FlatAppearance.BorderSize = 0;
        button.Click += onClick;
        return button;
    }

    private Control CreateMetricCard(string title, Label valueLabel)
    {
        var card = new Panel
        {
            Width = 240,
            Height = 86,
            BackColor = Color.White,
            Margin = new Padding(0, 0, 12, 12),
            Padding = new Padding(14, 12, 14, 12),
            BorderStyle = BorderStyle.FixedSingle
        };

        var titleLabel = new Label
        {
            Text = title,
            Dock = DockStyle.Top,
            Height = 24,
            ForeColor = Color.FromArgb(51, 65, 85),
            Font = new Font("Segoe UI", 9F, FontStyle.Bold)
        };

        valueLabel.Dock = DockStyle.Fill;
        valueLabel.TextAlign = ContentAlignment.MiddleLeft;
        valueLabel.Font = new Font("Segoe UI Semibold", 20F, FontStyle.Bold);
        valueLabel.ForeColor = Color.FromArgb(15, 23, 42);
        valueLabel.Text = "0";

        card.Controls.Add(valueLabel);
        card.Controls.Add(titleLabel);
        return card;
    }

    private static void ConfigureReadOnlyGrid(DataGridView grid)
    {
        grid.ReadOnly = true;
        grid.AllowUserToAddRows = false;
        grid.AllowUserToDeleteRows = false;
        grid.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
        grid.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
        grid.MultiSelect = false;
        grid.RowHeadersVisible = false;
        grid.BackgroundColor = Color.White;
        grid.BorderStyle = BorderStyle.FixedSingle;
    }

    private Control ConfigureCombo(ComboBox combo, params string[] items)
    {
        combo.DropDownStyle = ComboBoxStyle.DropDownList;
        combo.Items.Clear();
        combo.Items.AddRange(items);
        combo.SelectedIndex = items.Length > 0 ? 0 : -1;
        combo.Width = 260;
        return combo;
    }

    private void AddLabeledInput(TableLayoutPanel form, int row, string label, Control input)
    {
        form.RowStyles.Add(new RowStyle(SizeType.AutoSize));

        var lbl = new Label
        {
            Text = label,
            Dock = DockStyle.Fill,
            TextAlign = ContentAlignment.MiddleLeft,
            AutoSize = true,
            Margin = new Padding(0, 8, 8, 8)
        };

        input.Dock = DockStyle.Fill;
        input.Margin = new Padding(0, 6, 0, 6);

        if (input is TextBox box)
        {
            box.PlaceholderText = label;
        }

        form.Controls.Add(lbl, 0, row);
        form.Controls.Add(input, 1, row);
    }

    private void OpenFolder(string folderPath)
    {
        Directory.CreateDirectory(folderPath);
        Process.Start(new ProcessStartInfo
        {
            FileName = folderPath,
            UseShellExecute = true
        });
    }

    private static string SafeComboValue(ComboBox combo, string fallback)
        => combo.SelectedItem?.ToString() ?? fallback;

    private static void SelectComboValue(ComboBox combo, string value)
    {
        for (var i = 0; i < combo.Items.Count; i++)
        {
            if (string.Equals(combo.Items[i]?.ToString(), value, StringComparison.OrdinalIgnoreCase))
            {
                combo.SelectedIndex = i;
                return;
            }
        }

        if (combo.Items.Count > 0 && combo.SelectedIndex < 0)
        {
            combo.SelectedIndex = 0;
        }
    }

    private static string GetGridCellString(DataGridView grid, string columnName)
    {
        if (grid.CurrentRow?.Cells[columnName]?.Value is null)
        {
            return string.Empty;
        }

        return grid.CurrentRow.Cells[columnName].Value?.ToString() ?? string.Empty;
    }

    private static bool ContainsIgnoreCase(string? source, string search)
        => !string.IsNullOrWhiteSpace(source)
            && source.Contains(search, StringComparison.OrdinalIgnoreCase);

    private static void SafeRun(string actionName, Action action)
    {
        try
        {
            action();
        }
        catch (Exception ex)
        {
            MessageBox.Show(
                $"{actionName} failed.\n\n{ex.Message}",
                "OpsDesk Career Suite",
                MessageBoxButtons.OK,
                MessageBoxIcon.Error);
        }
    }

    private void ResetTicketInputs()
    {
        _selectedTicketId = null;
        _txtTicketTitle.Clear();
        _txtTicketNotes.Clear();
        _cmbTicketCategory.SelectedIndex = 0;
        _cmbTicketPriority.SelectedIndex = _cmbTicketPriority.Items.Count > 1 ? 1 : 0;
        _cmbTicketStatus.SelectedIndex = 0;
        _txtTicketOwner.Text = Environment.UserName;
        _txtTicketTitle.Focus();
    }

    private void ResetAssetInputs()
    {
        _selectedAssetId = null;
        _txtAssetName.Clear();
        _txtAssetTag.Clear();
        _txtAssetLocation.Clear();
        _txtAssetAssignedTo.Clear();
        _txtAssetNotes.Clear();
        _cmbAssetStatus.SelectedIndex = 0;
        _txtAssetName.Focus();
    }

    private static string Csv(string value)
    {
        value ??= string.Empty;
        var escaped = value.Replace("\"", "\"\"");
        return $"\"{escaped}\"";
    }

    private static SystemDriveInfo GetSystemDriveInfo()
    {
        try
        {
            var root = Path.GetPathRoot(Environment.SystemDirectory);
            var drive = new DriveInfo(root ?? "C:\\");
            return new SystemDriveInfo
            {
                RootPath = drive.Name,
                FreeGb = drive.AvailableFreeSpace / 1024d / 1024d / 1024d,
                TotalGb = drive.TotalSize / 1024d / 1024d / 1024d
            };
        }
        catch
        {
            return new SystemDriveInfo
            {
                RootPath = "Unknown",
                FreeGb = 0,
                TotalGb = 0
            };
        }
    }

    private static List<string> GetLocalIpv4Addresses()
    {
        try
        {
            return Dns.GetHostEntry(Dns.GetHostName())
                .AddressList
                .Where(x => x.AddressFamily == AddressFamily.InterNetwork)
                .Select(x => x.ToString())
                .Distinct()
                .ToList();
        }
        catch
        {
            return new List<string>();
        }
    }

    private static string GetHealthLabel(SystemDriveInfo drive)
    {
        if (drive.TotalGb <= 0) return "Warning";
        var freePercent = (drive.FreeGb / drive.TotalGb) * 100d;
        return freePercent < 10d ? "Warning" : "Healthy";
    }

    private static string GetHealthNotes(SystemDriveInfo drive)
    {
        if (drive.TotalGb <= 0) return "System drive metrics could not be read.";
        var freePercent = (drive.FreeGb / drive.TotalGb) * 100d;
        return freePercent < 10d
            ? "Free disk space is below 10%. Cleanup is recommended."
            : "Core checks completed successfully. Disk space is within normal range.";
    }

    private static string NextTicketId() => $"TKT-{DateTime.Now:yyyyMMddHHmmssfff}-{Guid.NewGuid().ToString("N")[..6].ToUpperInvariant()}";

    private static string NextAssetId() => $"AST-{DateTime.Now:yyyyMMddHHmmssfff}-{Guid.NewGuid().ToString("N")[..6].ToUpperInvariant()}";

    private static string BuildRoleMatchGuide()
    {
        var sb = new StringBuilder();
        sb.AppendLine("OPS DESK CAREER SUITE :: ROLE MATCH");
        sb.AppendLine(new string('=', 72));
        sb.AppendLine();
        sb.AppendLine("1. IT SUPPORT / HELPDESK");
        sb.AppendLine("- Use the Dashboard to show endpoint checks, user context, uptime, and disk health.");
        sb.AppendLine("- Use the Tickets tab to demonstrate issue intake, prioritization, status updates, and closure flow.");
        sb.AppendLine("- Show that your notes are clear, structured, and easy for handover.");
        sb.AppendLine();
        sb.AppendLine("2. NETWORK SUPPORT");
        sb.AppendLine("- Run Network Checks to validate reachability to internal and external targets.");
        sb.AppendLine("- Explain roundtrip time, failed reachability, and how you would escalate to device, DNS, or firewall checks.");
        sb.AppendLine();
        sb.AppendLine("3. IT GENERALIST / OPERATIONS");
        sb.AppendLine("- Combine audit, ticketing, and asset inventory to show all-around operational support capability.");
        sb.AppendLine("- Export reports as proof of documentation and operational discipline.");
        sb.AppendLine();
        sb.AppendLine("4. DATABASE / SYSTEM OPERATIONS");
        sb.AppendLine("- Explain that this version uses JSON storage for portability, but the workflow is already structured for database migration.");
        sb.AppendLine("- Mention that the next step is replacing JSON persistence with SQLite or SQL Server.");
        sb.AppendLine();
        sb.AppendLine("5. JUNIOR DEVELOPER / INTERNAL TOOLS");
        sb.AppendLine("- Show CRUD logic: add, update, search, resolve, delete, and export.");
        sb.AppendLine("- Explain the UI layout, model classes, JSON serialization, filtering, and report generation.");
        sb.AppendLine();
        sb.AppendLine("6. ADMIN / DOCUMENT CONTROL");
        sb.AppendLine("- Highlight orderly data entry, owner assignment, asset status, and CSV export for reporting.");
        sb.AppendLine();
        sb.AppendLine("INTERVIEW TIP");
        sb.AppendLine("- Never claim this is a full ITSM platform. Say it is a practical internal operations tool and portfolio project that proves workflow understanding and implementation ability.");
        return sb.ToString();
    }

    private static string BuildInterviewGuide()
    {
        var sb = new StringBuilder();
        sb.AppendLine("OPS DESK CAREER SUITE :: INTERVIEW + PRESENTATION GUIDE");
        sb.AppendLine(new string('=', 76));
        sb.AppendLine();
        sb.AppendLine("ELEVATOR PITCH (30-45 SECONDS)");
        sb.AppendLine("This is a Windows desktop application I built in C# and .NET 8 to simulate daily IT operations. It combines endpoint audit, network diagnostics, helpdesk ticket handling, asset administration, and reporting in one workflow. I use it to show that I understand both technical troubleshooting and structured operational documentation.");
        sb.AppendLine();
        sb.AppendLine("DEMO FLOW (2-4 MINUTES)");
        sb.AppendLine("1. Dashboard");
        sb.AppendLine("   - Click Refresh Audit.");
        sb.AppendLine("   - Explain machine context, uptime, IPv4, and disk health.");
        sb.AppendLine("   - Say: This is the first-line check I would do before deeper troubleshooting.");
        sb.AppendLine();
        sb.AppendLine("2. Network Checks");
        sb.AppendLine("   - Click Run Network Checks.");
        sb.AppendLine("   - Explain host reachability and roundtrip time.");
        sb.AppendLine("   - Say: If a host fails, I continue with adapter, DNS, gateway, or firewall checks.");
        sb.AppendLine();
        sb.AppendLine("3. Tickets");
        sb.AppendLine("   - Click Seed Demo Data if the list is empty.");
        sb.AppendLine("   - Open the Tickets tab.");
        sb.AppendLine("   - Add a ticket, then double-click an existing row, update status, and mark it Resolved.");
        sb.AppendLine("   - Say: This shows intake, prioritization, ownership, progress tracking, and closure.");
        sb.AppendLine();
        sb.AppendLine("4. Assets");
        sb.AppendLine("   - Open the Assets tab.");
        sb.AppendLine("   - Add or update an asset record.");
        sb.AppendLine("   - Say: This supports inventory accuracy, accountability, and maintenance visibility.");
        sb.AppendLine();
        sb.AppendLine("5. Export");
        sb.AppendLine("   - Return to Dashboard and click Export Report.");
        sb.AppendLine("   - Say: I can produce JSON, CSV, and HTML evidence for documentation and handover.");
        sb.AppendLine();
        sb.AppendLine("SKILLS TO MENTION");
        sb.AppendLine("- Troubleshooting mindset");
        sb.AppendLine("- Documentation discipline");
        sb.AppendLine("- Prioritization and issue handling");
        sb.AppendLine("- Asset and ownership tracking");
        sb.AppendLine("- C#/.NET desktop tool development");
        sb.AppendLine("- Data persistence and export automation");
        sb.AppendLine();
        sb.AppendLine("GOOD ANSWER IF ASKED: WHY DID YOU BUILD THIS?");
        sb.AppendLine("I built it to bridge support operations and software implementation. Many entry-level IT jobs need someone who can troubleshoot, document, and also improve internal workflows. This project proves I can do both.");
        sb.AppendLine();
        sb.AppendLine("GOOD ANSWER IF ASKED: WHAT WOULD YOU IMPROVE NEXT?");
        sb.AppendLine("My next step would be moving storage from JSON to SQLite or SQL Server, adding login and role-based access, and exposing the workflow through a REST API so the tool can scale beyond a local desktop scenario.");
        sb.AppendLine();
        sb.AppendLine("IMPORTANT");
        sb.AppendLine("- Speak clearly and practically.");
        sb.AppendLine("- Focus on what the app solves, not only the code.");
        sb.AppendLine("- Show one complete workflow from problem -> action -> documentation -> report.");
        sb.AppendLine("- Be honest that this is a portfolio-grade internal tool, not a commercial enterprise suite.");
        return sb.ToString();
    }
}

public sealed class TicketRecord
{
    public string Id { get; set; } = string.Empty;
    public DateTime CreatedAt { get; set; }
    public DateTime UpdatedAt { get; set; }
    public string Title { get; set; } = string.Empty;
    public string Category { get; set; } = string.Empty;
    public string Priority { get; set; } = string.Empty;
    public string Owner { get; set; } = string.Empty;
    public string Status { get; set; } = string.Empty;
    public string Notes { get; set; } = string.Empty;
}

public sealed class AssetRecord
{
    public string Id { get; set; } = string.Empty;
    public DateTime SavedAt { get; set; }
    public DateTime UpdatedAt { get; set; }
    public string DeviceName { get; set; } = string.Empty;
    public string AssetTag { get; set; } = string.Empty;
    public string Location { get; set; } = string.Empty;
    public string AssignedTo { get; set; } = string.Empty;
    public string Status { get; set; } = string.Empty;
    public string Notes { get; set; } = string.Empty;
}

public sealed class AuditSnapshot
{
    public DateTime CapturedAt { get; set; }
    public string MachineName { get; set; } = string.Empty;
    public string UserName { get; set; } = string.Empty;
    public string DomainName { get; set; } = string.Empty;
    public string OsDescription { get; set; } = string.Empty;
    public string Framework { get; set; } = string.Empty;
    public string ProcessArchitecture { get; set; } = string.Empty;
    public bool Is64Bit { get; set; }
    public int ProcessorCount { get; set; }
    public TimeSpan Uptime { get; set; }
    public string CurrentDirectory { get; set; } = string.Empty;
    public string SystemDirectory { get; set; } = string.Empty;
    public string SystemDrive { get; set; } = string.Empty;
    public double SystemDriveFreeGb { get; set; }
    public double SystemDriveTotalGb { get; set; }
    public List<string> LocalIpv4 { get; set; } = new();
    public string Health { get; set; } = string.Empty;
    public string Notes { get; set; } = string.Empty;
}

public sealed class NetworkProbe
{
    public string Host { get; set; } = string.Empty;
    public bool Reachable { get; set; }
    public long RoundtripMs { get; set; }
    public string Notes { get; set; } = string.Empty;
    public DateTime CheckedAt { get; set; }
}

public sealed class ExportBundle
{
    public AuditSnapshot? Audit { get; set; }
    public List<NetworkProbe> Network { get; set; } = new();
    public List<TicketRecord> Tickets { get; set; } = new();
    public List<AssetRecord> Assets { get; set; } = new();
    public ExportSummary? Summary { get; set; }
}

public sealed class ExportSummary
{
    public int TicketCount { get; set; }
    public int OpenTicketCount { get; set; }
    public int AssetCount { get; set; }
    public int ReachableHostCount { get; set; }
    public int TotalHostCount { get; set; }
    public DateTime GeneratedAt { get; set; }
    public string PortfolioPitch { get; set; } = string.Empty;
}

public sealed class SystemDriveInfo
{
    public string RootPath { get; set; } = string.Empty;
    public double FreeGb { get; set; }
    public double TotalGb { get; set; }
}
