using ClosedXML.Excel;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.OleDb;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace LFP_Manager.Function
{
    public class csExcelFunction
    {
        #region EXCEL IMPORT / EXPORT
        public static DataTable[] ExcelImport(string Ps_FileName, string[] sheetName)
        {
            DataTable[] result = null;

            try
            {
                string ExcelConn = string.Empty;

                if (Ps_FileName.IndexOf(".xlsx") > -1)      // 확장자에 따라서 provider 주의
                {
                    ExcelConn = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + Ps_FileName
                        + ";Extended Properties='Excel 12.0;HDR=YES'";
                }
                else
                {
                    ExcelConn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + Ps_FileName
                        + ";Extended Properties='Excel 8.0;HDR=YES'";
                }

                // 첫 번째 시트의 이름을 가져옮
                using (OleDbConnection con = new OleDbConnection(ExcelConn))
                {
                    using (OleDbCommand cmd = new OleDbCommand())
                    {
                        cmd.Connection = con;
                        con.Open();

                        DataTable dtExcelSchema = con.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);

                        if (dtExcelSchema.Rows.Count > 0)
                        {
                            sheetName = new string[dtExcelSchema.Rows.Count];

                            for (int i = 0; i < dtExcelSchema.Rows.Count; i++)
                                sheetName[i] = dtExcelSchema.Rows[i]["TABLE_NAME"].ToString();
                        }
                        con.Close();
                    }
                }
                string msg = string.Empty;
                for (int i = 0; i < sheetName.Length; i++)
                    msg += sheetName[i] + "\r\n";
                //MessageBox.Show("sheetName = " + msg);

                // 첫 번째 쉬트의 데이타를 읽어서 datagridview 에 보이게 함.
                using (OleDbConnection con = new OleDbConnection(ExcelConn))
                {
                    using (OleDbCommand cmd = new OleDbCommand())
                    {
                        using (OleDbDataAdapter oda = new OleDbDataAdapter())
                        {
                            result = new DataTable[sheetName.Length];

                            for (int i = 0; i < sheetName.Length; i++)
                            {
                                result[i] = new DataTable();
                                result[i].TableName = sheetName[i];
                                cmd.CommandText = "SELECT * From [" + sheetName[i] + "]";
                                cmd.Connection = con;
                                con.Open();
                                oda.SelectCommand = cmd;
                                try
                                {
                                    oda.Fill(result[i]);
                                }
                                catch (Exception)
                                {
                                    //MessageBox.Show(e1.Message);
                                }
                                con.Close();
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message, "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            return result;
        }

        public static void ExcelExport(string Ps_FileName, string[] sheetName)
        {
            string ExcelConn = string.Empty;

            if (Ps_FileName.IndexOf(".xlsx") > -1)      // 확장자에 따라서 provider 주의
            {
                ExcelConn = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + Ps_FileName
                    + ";Extended Properties='Excel 12.0;HDR=YES;IMEX=3;READONLY=FALSE'";
            }
            else
            {
                ExcelConn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + Ps_FileName
                    + ";Extended Properties='Excel 8.0;HDR=YES;IMEX=3;READONLY=FALSE'";
            }

            // 첫 번째 시트의 이름을 가져옮
            using (OleDbConnection con = new OleDbConnection(ExcelConn))
            {
                using (OleDbCommand cmd = new OleDbCommand())
                {
                    cmd.Connection = con;
                    con.Open();

                    DataTable dtExcelSchema = con.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);

                    if (dtExcelSchema.Rows.Count > 0)
                    {
                        sheetName = new string[dtExcelSchema.Rows.Count];

                        for (int i = 0; i < dtExcelSchema.Rows.Count; i++)
                            sheetName[i] = dtExcelSchema.Rows[i]["TABLE_NAME"].ToString();
                    }
                    con.Close();
                }
            }
            using (OleDbConnection connection = 
                new OleDbConnection(@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + @"C:\Users\[...]\Classeur.xls"
                    + ";Extended Properties=\"Excel 8.0;HDR=NO;IMEX=1;READONLY=FALSE\""))
            {
                connection.Open();
                OleDbCommand commande = new OleDbCommand(
                  "INSERT INTO [Feuil1$](F1,F2,F3) VALUES ('A3','B3','C3');", connection);
                commande.ExecuteNonQuery();

                connection.Close();
                connection.Dispose();
            }
        }
        #endregion

        #region EXCEL CLOSED XML IMPORT / EXPORT

        private static readonly string[] IsoDateFormats = new string[]
        {
            "yyyy-MM-dd HH:mm:ss",
            "yyyy-MM-ddTHH:mm:ss",
            "yyyy-MM-ddTHH:mm:ss.fff",
            "yyyy-MM-ddTHH:mm:ssZ",
            "yyyy-MM-ddTHH:mm:ss.fffZ",
            "yyyy-MM-dd"
        };

        public static bool ExportDgvWithClosedXml(string filePath, DataTable[] dt, bool overwiteFile = true)
        {
            if (dt == null || dt.Length == 0) return false;

            // .xls로 들어오면 .xlsx로 변환하여 저장 경로 결정
            var outputPath = EnsureXlsxPath(filePath);

            // 저장 경로 폴더 보장
            var dir = Path.GetDirectoryName(outputPath);
            if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
                Directory.CreateDirectory(dir);

            if (File.Exists(outputPath))
            {
                if (overwiteFile) File.Delete(outputPath);
                else throw new IOException("File already exists: " + outputPath);
            }

            try
            {
                using (var wb = new XLWorkbook())   // C# 7.3 호환
                {
                    var nameCount = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);

                    for (int i = 0; i < dt.Length; i++)
                    {
                        var table = dt[i];
                        if (table == null) continue;

                        string baseName = !string.IsNullOrWhiteSpace(table.TableName) ? table.TableName : ("Sheet" + (i + 1));
                        string sheetName = SanitizeSheetName(baseName);

                        // 중복 시트명 처리: _2, _3...
                        int dup;
                        if (nameCount.TryGetValue(sheetName, out dup))
                        {
                            dup++;
                            nameCount[sheetName] = dup;
                            string prefix = sheetName.Length > 28 ? sheetName.Substring(0, 28) : sheetName; // "_n" 자리 고려
                            sheetName = prefix + "_" + dup;
                        }
                        else
                        {
                            nameCount[sheetName] = 1;
                        }

                        // DataTable 전체를 시트로 추가 (헤더 포함)
                        var ws = wb.Worksheets.Add(table, sheetName);

                        // 자동 필터
                        var tbl = ws.Tables.FirstOrDefault();
                        if (tbl != null) tbl.ShowAutoFilter = true;

                        // ====== [추가] 문자열 날짜 → 진짜 DateTime으로 변환 ======
                        // 1) 날짜 컬럼 후보 탐지 (타입/컬럼명/샘플링)
                        bool[] isDateCol = new bool[table.Columns.Count];
                        for (int c = 0; c < table.Columns.Count; c++)
                        {
                            var colType = table.Columns[c].DataType;
                            if (colType == typeof(DateTime)) { isDateCol[c] = true; continue; }

                            if (colType == typeof(string))
                            {
                                string colName = table.Columns[c].ColumnName ?? "";
                                // 컬럼명 힌트
                                if (colName.IndexOf("date", StringComparison.OrdinalIgnoreCase) >= 0 ||
                                    colName.IndexOf("time", StringComparison.OrdinalIgnoreCase) >= 0 ||
                                    colName.EndsWith("_at", StringComparison.OrdinalIgnoreCase))
                                {
                                    isDateCol[c] = true;
                                    continue;
                                }

                                // 값 샘플링 (최대 20개)
                                int seen = 0, hits = 0;
                                for (int r = 0; r < table.Rows.Count && seen < 20; r++)
                                {
                                    var obj = table.Rows[r][c];
                                    if (obj == null || obj == DBNull.Value) continue;
                                    var s = obj.ToString();
                                    if (string.IsNullOrWhiteSpace(s)) continue;
                                    seen++;
                                    DateTime tmp;
                                    if (TryParseIsoDate(s, out tmp)) hits++;
                                }
                                if (seen > 0 && (hits * 1.0 / seen) >= 0.6) isDateCol[c] = true;
                            }
                        }

                        // 2) 열 서식 지정 + 각 셀 값 DateTime으로 재설정
                        for (int c = 0; c < table.Columns.Count; c++)
                        {
                            var col = ws.Column(c + 1);
                            Type t = table.Columns[c].DataType;

                            if (isDateCol[c])
                            {
                                // 열 서식(표시): 날짜/시간
                                col.Style.DateFormat.Format = "yyyy-MM-dd HH:mm:ss";

                                // 데이터 행(2행부터) 변환
                                int rowCount = table.Rows.Count;
                                for (int r = 0; r < rowCount; r++)
                                {
                                    var cell = ws.Cell(r + 2, c + 1);

                                    // 이미 DateTime 셀은 스킵
                                    if (cell.DataType == ClosedXML.Excel.XLDataType.DateTime)
                                        continue;

                                    var s = cell.GetString();
                                    if (string.IsNullOrWhiteSpace(s)) continue;

                                    DateTime dtv;
                                    if (TryParseIsoDate(s, out dtv))
                                    {
                                        cell.Value = dtv; // ✅ 진짜 날짜로 저장 (엑셀 DateTime)
                                                          // cell.Style.DateFormat.Format = "yyyy-MM-dd HH:mm:ss"; // per-cell 필요시
                                    }
                                }
                            }
                            else
                            {
                                // 기존 숫자/텍스트 서식 유지
                                if (t == typeof(DateTime))
                                    col.Style.DateFormat.Format = "yyyy-MM-dd HH:mm:ss";
                                else if (t == typeof(int) || t == typeof(long) || t == typeof(short) || t == typeof(byte))
                                    col.Style.NumberFormat.Format = "0";           // 정수
                                else if (t == typeof(decimal) || t == typeof(float) || t == typeof(double))
                                    col.Style.NumberFormat.Format = "General";     // 일반 숫자
                                else
                                    col.Style.NumberFormat.Format = "@";           // 텍스트
                            }
                        }
                        // ====== [추가 끝] ======

                        // 열 너비 자동 조정
                        ws.Columns().AdjustToContents();
                    }
                    wb.SaveAs(outputPath);
                }
            }
            catch (Exception ex)
            {
                // 필요 시 로깅 추가
                throw new Exception(ex.Message);
            }
            return true;
        }

        private static string EnsureXlsxPath(string filePath)
        {
            if (string.IsNullOrEmpty(filePath)) throw new ArgumentNullException(nameof(filePath));
            var ext = Path.GetExtension(filePath);
            if (!string.IsNullOrEmpty(ext) && ext.Equals(".xls", StringComparison.OrdinalIgnoreCase))
                return Path.ChangeExtension(filePath, ".xlsx");
            return filePath;
        }

        private static string SanitizeSheetName(string name)
        {
            if (string.IsNullOrEmpty(name)) return "Sheet1";
            // Excel 금지문자: : \ / ? * [ ]
            char[] invalid = { ':', '\\', '/', '?', '*', '[', ']' };
            var arr = name.ToCharArray();
            for (int i = 0; i < arr.Length; i++)
                for (int j = 0; j < invalid.Length; j++)
                    if (arr[i] == invalid[j]) { arr[i] = '_'; break; }

            var sanitized = new string(arr);
            if (sanitized.Length > 31) sanitized = sanitized.Substring(0, 31);
            if (string.IsNullOrWhiteSpace(sanitized)) sanitized = "Sheet1";
            return sanitized;
        }

        private static bool TryParseIsoDate(string s, out DateTime dt)
        {
            // 로컬 시간 가정(오프셋 보정 없음). DB 문자열이 UTC라면 AdjustToUniversal로 변경하세요.
            return DateTime.TryParseExact(
                       s, IsoDateFormats,
                       CultureInfo.InvariantCulture,
                       DateTimeStyles.AssumeLocal,
                       out dt)
                   || DateTime.TryParse(
                       s, CultureInfo.InvariantCulture,
                       DateTimeStyles.AssumeLocal,
                       out dt);
        }

        #endregion
    }
}
