using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Web.Script.Serialization;

namespace Ounibo.Catalog.Core
{
    /// <summary>
    /// 系列配置 (源目录 series\&lt;CODE&gt;.meta.json 的格式, 与向导 保存配置 完全同构)。
    /// </summary>
    public class SeriesMeta
    {
        public Series series { get; set; }
        public List<BuildAttachmentSpec> attachments { get; set; }
    }

    /// <summary>
    /// 全量目录构建器 (统一路线, 2026-08-22):
    /// 唯一数据源 = 源目录 (默认 onebot-data\catalog\), 结构:
    ///   catalog.json           目录定义 (名称/版本/分类/系列顺序, 与包内 catalog.json 同构, series 部分留空)
    ///   series\&lt;CODE&gt;.meta.json   每系列定义 (参数/规则/附件, 向导"保存配置"直接写这里)
    ///   csv\&lt;CODE&gt;.csv           参数表 (model_code + 参数列 + step_file 列)
    ///   step\*.step              STEP 文件 (几何生成器或 NX 导出)
    ///   manual\... 等附件源文件 (meta 里 attachments.source 为相对源目录的路径)
    /// 构建: 全部系列 → 一个 .opc (单一路线, 不再需要 PowerShell 打包)。
    /// </summary>
    public static class FullCatalogBuilder
    {
        public static BuildResult BuildFromSourceDir(string sourceDir, string outPath)
        {
            var r = new BuildResult { Ok = true };
            try
            {
                var catFile = Path.Combine(sourceDir, "catalog.json");
                if (!File.Exists(catFile)) { r.Errors.Add("源目录缺少 catalog.json: " + sourceDir); r.Ok = false; return r; }
                var ser = new JavaScriptSerializer { MaxJsonLength = int.MaxValue };
                var cat = ser.Deserialize<Catalog>(File.ReadAllText(catFile, Encoding.UTF8));
                if (cat == null || cat.categories == null || cat.categories.Count == 0)
                { r.Errors.Add("catalog.json 无效 (缺 categories)"); r.Ok = false; return r; }

                var stepDir = Path.Combine(sourceDir, "step");
                if (!Directory.Exists(stepDir)) { r.Errors.Add("STEP 目录不存在: " + stepDir); r.Ok = false; return r; }

                // 系列代码按分类顺序收集
                var codes = new List<string>();
                foreach (var c in cat.categories)
                    foreach (var sc in c.series ?? new List<string>())
                        if (!codes.Contains(sc)) codes.Add(sc);

                // 每系列: meta + CSV → 变体 + 附件
                var builtSeries = new List<Series>();
                var filesToCopy = new List<Tuple<string, string>>();   // (源绝对路径, 包内相对路径)
                foreach (var code in codes)
                {
                    var metaPath = Path.Combine(sourceDir, "series", code + ".meta.json");
                    if (!File.Exists(metaPath)) { r.Errors.Add("系列配置缺失: " + metaPath); continue; }
                    SeriesMeta meta;
                    try { meta = ser.Deserialize<SeriesMeta>(File.ReadAllText(metaPath, Encoding.UTF8)); }
                    catch (Exception ex) { r.Errors.Add("系列配置解析失败 " + code + ": " + ex.Message); continue; }
                    var s = meta == null ? null : meta.series;
                    if (s == null) { r.Errors.Add("系列配置无效: " + metaPath); continue; }

                    var csvPath = Path.Combine(sourceDir, "csv", code + ".csv");
                    if (!File.Exists(csvPath)) { r.Errors.Add("CSV 缺失: " + csvPath); continue; }
                    var vr = new BuildResult { Ok = true };
                    var variants = OpcBuilder.ReadCsvVariants(s, csvPath, stepDir, vr);
                    foreach (var e in vr.Errors) r.Errors.Add("[" + code + "] " + e);
                    if (variants == null || variants.Count == 0) { r.Errors.Add("系列 " + code + " 无有效变体"); continue; }
                    s.variants = variants;

                    // 参数化系列 (mode=parametric): 域校验 + 模板对拍门禁 (CSV 全部编码必须能被模板重新生成且一一对应)
                    if (ParametricEngine.IsParametric(s))
                    {
                        if (string.IsNullOrEmpty(s.modelCodeTemplate))
                            r.Errors.Add("[" + code + "] 参数化系列缺少 modelCodeTemplate");
                        foreach (var p in s.parameters ?? new List<ParameterDef>())
                        {
                            if (p.type != "range") continue;
                            if (p.min == null || p.max == null || p.min.Value > p.max.Value)
                            { r.Errors.Add("[" + code + "] 参数 " + p.code + " range 域非法 (min/max)"); continue; }
                            if ((p.step ?? 1) <= 0) { r.Errors.Add("[" + code + "] 参数 " + p.code + " step 非法"); continue; }
                            foreach (var gv in p.gridValues ?? new List<object>())
                                if (!ParametricEngine.DomainValid(p, gv))
                                    r.Errors.Add("[" + code + "] 参数 " + p.code + " gridValues 含域外值: " + gv);
                        }
                        if (!string.IsNullOrEmpty(s.modelCodeTemplate))
                        {
                            var codeToSig = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
                            foreach (var v in variants)
                            {
                                var regen = ParametricEngine.RenderCode(s.modelCodeTemplate, v.@params);
                                if (regen == null || !string.Equals(regen, v.modelCode, StringComparison.OrdinalIgnoreCase))
                                    r.Errors.Add("[" + code + "] 对拍门禁失败: " + v.modelCode + " 模板重生成=" + (regen ?? "null"));
                                else
                                {
                                    var sig = string.Join("|", (s.parameters ?? new List<ParameterDef>())
                                        .Select(x => x.code + "=" + (v.@params.ContainsKey(x.code) ? v.@params[x.code].ToString() : "")));
                                    string prev;
                                    if (codeToSig.TryGetValue(regen, out prev))
                                    { if (prev != sig) r.Errors.Add("[" + code + "] 模板多对一: " + regen + " 对应两个不同组合"); }
                                    else codeToSig[regen] = sig;
                                }
                            }
                            if (r.Errors.Count == 0)
                                r.Warnings.Add("[" + code + "] 参数化对拍门禁通过: " + variants.Count + " 个编码全部一致");
                        }
                    }

                    // 附件: 源文件(相对源目录) → 包内路径 (manual 走 manual/<系列>/ 布局)
                    s.attachments = new List<Attachment>();
                    foreach (var a in meta.attachments ?? new List<BuildAttachmentSpec>())
                    {
                        if (string.IsNullOrEmpty(a.source)) continue;
                        var src = Path.Combine(sourceDir, a.source.Replace('/', '\\'));
                        if (!File.Exists(src)) { r.Warnings.Add("附件缺失, 跳过: " + a.source); continue; }
                        string rel;
                        if (a.kind == "manual")
                            rel = "manual/" + code + "/" + Path.GetFileName(a.source);
                        else
                        {
                            var folder = a.kind == "dimDrawing" ? "dim-drawing" : a.kind;
                            rel = "docs/" + folder + "/" + Path.GetFileName(a.source);
                        }
                        s.attachments.Add(new Attachment { kind = a.kind, lang = a.lang, path = rel, model = a.model });
                        filesToCopy.Add(new Tuple<string, string>(src, rel));
                    }
                    builtSeries.Add(s);
                }
                if (builtSeries.Count == 0) { r.Errors.Add("无有效系列"); r.Ok = false; return r; }
                if (r.Errors.Count > 0) { r.Ok = false; return r; }

                cat.series = builtSeries;

                // 平替对照表 (可选): catalog\crossref.csv → 校验我方型号存在 → 嵌入 catalog.json
                var crossRefPath = Path.Combine(sourceDir, "crossref.csv");
                if (File.Exists(crossRefPath))
                {
                    var validCodes = new HashSet<string>(
                        builtSeries.SelectMany(s => s.variants ?? new List<Variant>()).Select(v => v.modelCode));
                    var rows = new List<CrossRefRow>();
                    var crLines = File.ReadAllLines(crossRefPath, Encoding.UTF8)
                        .Where(l => !string.IsNullOrWhiteSpace(l)).ToArray();
                    if (crLines.Length > 1)
                    {
                        var crHeader = crLines[0].Split(',').Select(x => x.Trim()).ToArray();
                        var colBrand = Array.IndexOf(crHeader, "brand");
                        var colForeign = Array.IndexOf(crHeader, "foreign_model");
                        var colOur = Array.IndexOf(crHeader, "our_model");
                        var colNote = Array.IndexOf(crHeader, "note");
                        if (colBrand < 0 || colForeign < 0 || colOur < 0)
                            r.Errors.Add("crossref.csv 表头需含 brand,foreign_model,our_model 列");
                        else
                        {
                            for (int i = 1; i < crLines.Length; i++)
                            {
                                var c = crLines[i].Split(',');
                                if (c.Length < 4) { r.Errors.Add("crossref.csv 第 " + (i + 1) + " 行字段不足"); continue; }
                                var brand = c[colBrand].Trim();
                                var foreignModel = c[colForeign].Trim();
                                var ourModel = c[colOur].Trim();
                                if (string.IsNullOrEmpty(brand) || string.IsNullOrEmpty(foreignModel) || string.IsNullOrEmpty(ourModel))
                                { r.Errors.Add("crossref.csv 第 " + (i + 1) + " 行有空字段"); continue; }
                                if (!validCodes.Contains(ourModel))
                                { r.Errors.Add("crossref.csv: 我方型号不存在 " + ourModel + " (" + brand + " " + foreignModel + ")"); continue; }
                                rows.Add(new CrossRefRow
                                {
                                    brand = brand,
                                    foreignModel = foreignModel,
                                    ourModel = ourModel,
                                    note = (colNote >= 0 && colNote < c.Length) ? c[colNote].Trim() : ""
                                });
                            }
                        }
                    }
                    if (r.Errors.Count > 0) { r.Ok = false; return r; }
                    cat.crossRefs = rows;
                }

                // 参数化测试向量 (双端对拍, 2026-08-25): 源目录根 parametric-vectors.json → 包内根
                // 桌面 selftest 与网页无头 E2E 读同一份文件对拍, 保证 C#/JS 解释器语义一致
                var vecPath = Path.Combine(sourceDir, "parametric-vectors.json");
                if (File.Exists(vecPath))
                {
                    try
                    {
                        ser.DeserializeObject(File.ReadAllText(vecPath, Encoding.UTF8));
                        filesToCopy.Add(new Tuple<string, string>(vecPath, "parametric-vectors.json"));
                    }
                    catch (Exception ex) { r.Errors.Add("parametric-vectors.json 解析失败: " + ex.Message); }
                }

                // 组装临时目录 → zip
                var tmp = Path.Combine(Path.GetTempPath(), "opcbuild_" + Guid.NewGuid().ToString("N"));
                Directory.CreateDirectory(tmp);
                try
                {
                    foreach (var s in builtSeries)
                        foreach (var v in s.variants ?? new List<Variant>())
                        {
                            var dest = Path.Combine(tmp, v.step.Replace('/', '\\'));
                            Directory.CreateDirectory(Path.GetDirectoryName(dest));
                            var srcName = OpcBuilder.ResolveStepFile(stepDir, Path.GetFileName(v.step.Replace('/', '\\')));
                            File.Copy(Path.Combine(stepDir, srcName), dest, true);
                        }
                    foreach (var f in filesToCopy)
                    {
                        var dest = Path.Combine(tmp, f.Item2.Replace('/', '\\'));
                        Directory.CreateDirectory(Path.GetDirectoryName(dest));
                        File.Copy(f.Item1, dest, true);
                    }

                    File.WriteAllText(Path.Combine(tmp, "catalog.json"), ser.Serialize(cat), new UTF8Encoding(true));

                    Directory.CreateDirectory(Path.GetDirectoryName(outPath));
                    if (File.Exists(outPath)) File.Delete(outPath);
                    ZipFile.CreateFromDirectory(tmp, outPath, CompressionLevel.Optimal, false);
                    r.OutputPath = outPath;
                }
                finally
                {
                    Directory.Delete(tmp, true);
                }
            }
            catch (Exception ex)
            {
                r.Errors.Add("全量打包异常: " + ex.Message);
                r.Ok = false;
            }
            return r;
        }
    }
}
