using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using Ounibo.Catalog.Core;

namespace Ounibo.Catalog.App
{
    /// <summary>嵌入资源加载 (logo 等编译进 exe 的资源)。</summary>
    public static class AppResources
    {
        public static System.Windows.Media.Imaging.BitmapImage LoadPng(string name)
        {
            try
            {
                var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name);
                if (stream == null) return null;
                var bmp = new System.Windows.Media.Imaging.BitmapImage();
                bmp.BeginInit();
                bmp.StreamSource = stream;
                bmp.CacheOption = System.Windows.Media.Imaging.BitmapCacheOption.OnLoad;
                bmp.EndInit();
                return bmp;
            }
            catch { return null; }
        }
    }

    /// <summary>
    /// 程序入口。
    /// 开发版: OnebotCatalog.exe [数据包.opc] [--admin] [--selftest 数据包.opc]
    /// 客户版 (CUSTOMER_BUILD): 启动时先找 exe 旁 catalog.opc, 找不到读嵌入资源;
    /// 无维护入口、无自测。
    /// </summary>
    public static class Program
    {
        [STAThread]
        public static void Main(string[] args)
        {
#if !CUSTOMER_BUILD
            // 自测模式: 无界面运行核心逻辑, 结果写 selftest.log
            if (args.Length >= 2 && args[0] == "--selftest")
            {
                Environment.ExitCode = SelfTest.Run(args[1]);
                return;
            }

            // CLI 打包模式 (与向导同一 Builder 内核):
            //   --build <csv> <stepDir> <out.opc> [--series CODE] [--meta 元数据.json]
            // 无 --meta 时参数自动识别 (无规则/附件/关键词); 结果写 <out 同目录>\build.log
            if (args.Length >= 4 && args[0] == "--build")
            {
                var csv = args[1]; var stepDir = args[2]; var outP = args[3];
                string seriesCode = "S1";
                string metaPath = null;
                for (int i = 4; i + 1 < args.Length; i++)
                {
                    if (args[i] == "--series") seriesCode = args[i + 1];
                    if (args[i] == "--meta") metaPath = args[i + 1];
                }

                Series series;
                var atts = new List<BuildAttachmentSpec>();
                if (metaPath != null)
                {
                    var ser = new System.Web.Script.Serialization.JavaScriptSerializer();
                    var meta = ser.Deserialize<SeriesMeta>(File.ReadAllText(metaPath, Encoding.UTF8));
                    series = meta.series ?? new Series();
                    if (series.parameters == null || series.parameters.Count == 0)
                        series.parameters = OpcBuilder.AutoDetectParameters(csv);
                    atts = meta.attachments ?? new List<BuildAttachmentSpec>();
                }
                else
                {
                    series = new Series { parameters = OpcBuilder.AutoDetectParameters(csv) };
                }
                series.code = string.IsNullOrEmpty(series.code) ? seriesCode : series.code;
                series.nameZh = string.IsNullOrEmpty(series.nameZh) ? seriesCode : series.nameZh;
                series.nameEn = string.IsNullOrEmpty(series.nameEn) ? seriesCode : series.nameEn;
                series.naming = series.naming ?? new Naming { stepNameTemplate = "{model}.step" };
                series.attachments = series.attachments ?? new List<Attachment>();
                series.variants = new List<Variant>();

                var res = OpcBuilder.Build(outP, "欧霓博气动目录", "0.1", "ACT", "产品", "Products",
                    series, csv, stepDir, atts);
                var sb = new StringBuilder();
                sb.AppendLine(res.Ok ? "BUILD OK: " + outP : "BUILD FAILED");
                foreach (var e in res.Errors) sb.AppendLine("  [错误] " + e);
                foreach (var w in res.Warnings) sb.AppendLine("  [警告] " + w);
                if (res.Ok)
                {
                    try
                    {
                        using (var built = OpcPackage.Open(outP))
                        {
                            var issues = PackageValidator.Validate(built);
                            sb.AppendLine("变体数: " + built.Catalog.series.Sum(s2 => s2.variants.Count));
                            sb.AppendLine("校验: " + issues.Count(i => i.Severity == "ERROR") + " 错误, " + issues.Count(i => i.Severity == "WARNING") + " 警告");
                            foreach (var i in issues) sb.AppendLine("  [" + i.Severity + "] " + i.Target + " " + i.Message);
                        }
                    }
                    catch (Exception ex) { sb.AppendLine("产物校验失败: " + ex.Message); Environment.ExitCode = 1; return; }
                }
                else Environment.ExitCode = 1;
                File.WriteAllText(Path.Combine(Path.GetDirectoryName(outP) ?? ".", "build.log"), sb.ToString(), Encoding.UTF8);
                return;
            }

            // CLI 全量打包 (统一路线): --buildfull <源目录> <out.opc>
            // 源目录结构见 FullCatalogBuilder; 结果写 <out 同目录>\build.log
            if (args.Length >= 3 && args[0] == "--buildfull")
            {
                var res = FullCatalogBuilder.BuildFromSourceDir(args[1], args[2]);
                var sb = new StringBuilder();
                sb.AppendLine(res.Ok ? "BUILDFULL OK: " + args[2] : "BUILDFULL FAILED");
                foreach (var e in res.Errors) sb.AppendLine("  [错误] " + e);
                foreach (var w in res.Warnings) sb.AppendLine("  [警告] " + w);
                if (res.Ok)
                {
                    try
                    {
                        using (var built = OpcPackage.Open(args[2]))
                        {
                            sb.AppendLine("系列数: " + built.Catalog.series.Count + " | 变体数: " + built.Catalog.series.Sum(s2 => s2.variants.Count));
                            var issues = PackageValidator.Validate(built);
                            sb.AppendLine("校验: " + issues.Count(i => i.Severity == "ERROR") + " 错误, " + issues.Count(i => i.Severity == "WARNING") + " 警告");
                            foreach (var i in issues) sb.AppendLine("  [" + i.Severity + "] " + i.Target + " " + i.Message);
                        }
                    }
                    catch (Exception ex) { sb.AppendLine("产物校验失败: " + ex.Message); Environment.ExitCode = 1; }
                }
                else Environment.ExitCode = 1;
                File.WriteAllText(Path.Combine(Path.GetDirectoryName(args[2]) ?? ".", "build.log"), sb.ToString(), Encoding.UTF8);
                return;
            }
#endif

            string opc = null;
            foreach (var a in args)
            {
                if (a.EndsWith(".opc", StringComparison.OrdinalIgnoreCase)) opc = a;
            }

#if CUSTOMER_BUILD
            OpcPackage pkg = null;
            if (opc != null)
            {
                try { pkg = OpcPackage.Open(opc); }
                catch (Exception ex) { ShowFatal(ex.Message); return; }
            }
            else pkg = TryOpenEmbedded();
            if (pkg == null)
            {
                // 无嵌入数据且无外置数据包 → 让用户选择 .opc
                var dlg = new Microsoft.Win32.OpenFileDialog { Filter = "目录数据包 (*.opc)|*.opc", Title = "选择 .opc 数据包" };
                if (dlg.ShowDialog() == true)
                {
                    try { pkg = OpcPackage.Open(dlg.FileName); }
                    catch (Exception ex) { ShowFatal(ex.Message); return; }
                }
                else return;
            }
            Run(pkg, false);
#else
            bool admin = false;
            foreach (var a in args) if (a == "--admin") admin = true;
            if (admin && !PromptPassword())
                return;
            if (opc == null)
                opc = FindDefaultPackage();
            OpcPackage pkg = null;
            if (opc != null)
            {
                try { pkg = OpcPackage.Open(opc); }
                catch (Exception ex) { ShowFatal(ex.Message); return; }
            }
            else pkg = TryOpenEmbedded();
            Run(pkg, admin);
#endif
        }

        static void Run(OpcPackage pkg, bool admin)
        {
            var app = new Application { ShutdownMode = ShutdownMode.OnMainWindowClose };
            // 全局异常日志 (crash.log, 与 exe 同目录), UI 异常不崩溃
            var logPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "crash.log");
            app.DispatcherUnhandledException += (s, e) =>
            {
                try { File.AppendAllText(logPath, DateTime.Now + " UI异常: " + e.Exception + "\r\n", Encoding.UTF8); } catch { }
                e.Handled = true;
            };
            AppDomain.CurrentDomain.UnhandledException += (s, e) =>
            {
                try { File.AppendAllText(logPath, DateTime.Now + " 未处理异常: " + e.ExceptionObject + "\r\n", Encoding.UTF8); } catch { }
            };
            app.Run(new MainWindow(pkg, admin));
        }

        static void ShowFatal(string msg)
        {
            MessageBox.Show("数据包打开失败: " + msg, "欧霓博气动目录 ONEBOT Catalog", MessageBoxButton.OK, MessageBoxImage.Error);
        }

        /// <summary>
        /// 先找 exe 旁 catalog.opc (外置数据包, 支持增量更新), 找不到读嵌入资源。
        /// </summary>
        static OpcPackage TryOpenEmbedded()
        {
            var external = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "catalog.opc");
            if (File.Exists(external))
            {
                try { return OpcPackage.Open(external); }
                catch { /* 外置包损坏则回退嵌入资源 */ }
            }
            try
            {
                var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("catalog.opc");
                if (stream != null)
                    return OpcPackage.OpenStream(stream, "内置数据包 (catalog.opc)");
            }
            catch { /* 无嵌入资源 */ }
            return null;
        }

#if !CUSTOMER_BUILD
        static string FindDefaultPackage()
        {
            var dirs = new[]
            {
                AppDomain.CurrentDomain.BaseDirectory,
                Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "sample"),
                Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..", "sample")
            };
            foreach (var d in dirs)
            {
                try
                {
                    if (!Directory.Exists(d)) continue;
                    var f = Directory.GetFiles(d, "*.opc").FirstOrDefault();
                    if (f != null) return f;
                }
                catch { /* 忽略目录访问失败 */ }
            }
            return null;
        }

        /// <summary>
        /// 维护入口密码。TODO: 移到配置文件, 支持修改。
        /// </summary>
        static bool PromptPassword()
        {
            var win = new Window
            {
                Title = "维护入口 Admin",
                Width = 360, Height = 180, MinWidth = 320, MinHeight = 150,
                WindowStartupLocation = WindowStartupLocation.CenterScreen
            };
            var panel = new StackPanel { Margin = new Thickness(16) };
            panel.Children.Add(new TextBlock { Text = "请输入维护密码 / Enter admin password:", Margin = new Thickness(0, 0, 0, 8) });
            var box = new PasswordBox { Margin = new Thickness(0, 0, 0, 12) };
            panel.Children.Add(box);
            var btns = new StackPanel { Orientation = Orientation.Horizontal };
            var ok = new Button { Content = "确定 OK", Width = 90, Margin = new Thickness(0, 0, 8, 0), IsDefault = true };
            var cancel = new Button { Content = "取消 Cancel", Width = 90, IsCancel = true };
            btns.Children.Add(ok); btns.Children.Add(cancel);
            panel.Children.Add(btns);
            win.Content = panel;

            bool granted = false;
            ok.Click += (s, e) =>
            {
                if (box.Password == "onebot888") { granted = true; win.Close(); }
                else MessageBox.Show("密码错误 / Wrong password", "维护入口", MessageBoxButton.OK, MessageBoxImage.Warning);
            };
            win.ShowDialog();
            return granted;
        }
#endif
    }
}
