using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace Электронная_Фармация.Classes
{
    /// <summary>
    /// Лёгкая подсветка SQL в RichTextBox (ключевые слова, строки, комментарии, числа, параметры).
    /// </summary>
    public static class SqlSyntaxHighlighter
    {
        private static readonly Regex TokenRegex = new Regex(
            @"(--[^\r\n]*)|(/\*[\s\S]*?\*/)|('(?:''|[^'])*')|(""(?:""""|[^""])*"")|(\[[^\]]*\])|(@[A-Za-z_][A-Za-z0-9_]*)|(\{\{[A-Za-z_][A-Za-z0-9_]*\}\})|(\b\d+(?:\.\d+)?\b)|(\b[A-Za-z_][A-Za-z0-9_]*\b)",
            RegexOptions.Compiled);

        private static readonly System.Collections.Generic.HashSet<string> Keywords =
            new System.Collections.Generic.HashSet<string>(StringComparer.OrdinalIgnoreCase)
            {
                "SELECT", "FROM", "WHERE", "AND", "OR", "NOT", "IN", "IS", "NULL", "AS",
                "JOIN", "INNER", "LEFT", "RIGHT", "OUTER", "CROSS", "ON", "USING",
                "GROUP", "BY", "ORDER", "HAVING", "LIMIT", "OFFSET", "DISTINCT", "ALL",
                "INSERT", "INTO", "VALUES", "UPDATE", "SET", "DELETE", "REPLACE",
                "CREATE", "ALTER", "DROP", "TABLE", "INDEX", "VIEW", "TRIGGER", "IF", "EXISTS",
                "PRIMARY", "KEY", "FOREIGN", "REFERENCES", "UNIQUE", "DEFAULT", "CHECK",
                "BEGIN", "COMMIT", "ROLLBACK", "TRANSACTION", "PRAGMA", "EXPLAIN", "WITH",
                "CASE", "WHEN", "THEN", "ELSE", "END", "BETWEEN", "LIKE", "GLOB", "ESCAPE",
                "UNION", "INTERSECT", "EXCEPT", "CAST", "COLLATE", "ASC", "DESC",
                "COUNT", "SUM", "AVG", "MIN", "MAX", "IFNULL", "COALESCE", "NULLIF",
                "LENGTH", "TRIM", "UPPER", "LOWER", "SUBSTR", "REPLACE", "ROUND", "ABS",
                "DATETIME", "DATE", "TIME", "STRFTIME", "ADD", "COLUMN", "RENAME", "TO"
            };

        private const int WM_SETREDRAW = 0x000B;

        [DllImport("user32.dll")]
        private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

        public static void Apply(RichTextBox box, bool darkTheme)
        {
            if (box == null || box.IsDisposed)
            {
                return;
            }

            string text = box.Text ?? string.Empty;
            int selStart = box.SelectionStart;
            int selLength = box.SelectionLength;
            int firstVisibleChar = box.GetCharIndexFromPosition(new Point(1, 1));

            Color defaultFg = darkTheme ? Color.FromArgb(220, 220, 220) : Color.FromArgb(30, 30, 30);
            Color keyword = darkTheme ? Color.FromArgb(86, 156, 214) : Color.FromArgb(0, 0, 180);
            Color str = darkTheme ? Color.FromArgb(206, 145, 120) : Color.FromArgb(163, 21, 21);
            Color comment = darkTheme ? Color.FromArgb(106, 153, 85) : Color.FromArgb(0, 128, 0);
            Color number = darkTheme ? Color.FromArgb(181, 206, 168) : Color.FromArgb(9, 134, 88);
            Color param = darkTheme ? Color.FromArgb(197, 134, 192) : Color.FromArgb(128, 0, 128);
            Color ident = darkTheme ? Color.FromArgb(156, 220, 254) : Color.FromArgb(0, 100, 120);

            SuspendRedraw(box);
            try
            {
                box.SelectAll();
                box.SelectionColor = defaultFg;
                box.SelectionFont = box.Font;

                foreach (Match match in TokenRegex.Matches(text))
                {
                    Color color = defaultFg;
                    bool bold = false;

                    if (match.Groups[1].Success || match.Groups[2].Success)
                    {
                        color = comment;
                    }
                    else if (match.Groups[3].Success || match.Groups[4].Success || match.Groups[5].Success)
                    {
                        color = str;
                    }
                    else if (match.Groups[6].Success || match.Groups[7].Success)
                    {
                        color = param;
                        bold = true;
                    }
                    else if (match.Groups[8].Success)
                    {
                        color = number;
                    }
                    else if (match.Groups[9].Success)
                    {
                        var word = match.Groups[9].Value;
                        if (Keywords.Contains(word))
                        {
                            color = keyword;
                            bold = true;
                        }
                        else
                        {
                            color = ident;
                        }
                    }

                    if (color == defaultFg && !bold)
                    {
                        continue;
                    }

                    box.Select(match.Index, match.Length);
                    box.SelectionColor = color;
                    if (bold)
                    {
                        box.SelectionFont = new Font(box.Font, FontStyle.Bold);
                    }
                    else
                    {
                        box.SelectionFont = box.Font;
                    }
                }
            }
            finally
            {
                box.Select(selStart, selLength);
                try
                {
                    box.Select(firstVisibleChar, 0);
                    var pt = box.GetPositionFromCharIndex(firstVisibleChar);
                    if (pt.Y < 0)
                    {
                        // keep best-effort scroll
                    }
                }
                catch
                {
                    // ignore scroll restore issues
                }

                box.Select(selStart, selLength);
                ResumeRedraw(box);
                box.Invalidate();
            }
        }

        private static void SuspendRedraw(Control control)
        {
            SendMessage(control.Handle, WM_SETREDRAW, IntPtr.Zero, IntPtr.Zero);
        }

        private static void ResumeRedraw(Control control)
        {
            SendMessage(control.Handle, WM_SETREDRAW, new IntPtr(1), IntPtr.Zero);
        }
    }
}
