using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
using Npgsql;

namespace ConstructionApp
{
    public class Form1 : Form
    {
        private string connString;
        private string schema = "mitrushina_vs";
        private DataGridView dgv = new DataGridView();
        private ComboBox cbTables = new ComboBox();
        private TextBox txtSearch = new TextBox();
        private Label lblStatus = new Label();
        private TableLayoutPanel topPanel = new TableLayoutPanel();

        public Form1()
        {
            ShowLogin();
        }

        private void ShowLogin()
        {
            var form = new Form();
            form.Text = "Вход";
            form.Size = new Size(300, 200);
            form.StartPosition = FormStartPosition.CenterScreen;
            form.FormBorderStyle = FormBorderStyle.FixedDialog;

            var lblUser = new Label() { Text = "Логин:", Location = new Point(20, 30), Size = new Size(60, 25) };
            var txtUser = new TextBox() { Location = new Point(80, 30), Size = new Size(180, 23) };
            var lblPass = new Label() { Text = "Пароль:", Location = new Point(20, 65), Size = new Size(60, 25) };
            var txtPass = new TextBox() { Location = new Point(80, 65), Size = new Size(180, 23), PasswordChar = '*' };
            var btnLogin = new Button() { Text = "Вход", Location = new Point(100, 110), Size = new Size(80, 30) };
            var btnExit = new Button() { Text = "Выход", Location = new Point(190, 110), Size = new Size(80, 30) };

            btnLogin.Click += (s, e) =>
            {
                try
                {
                    connString = $"Host=80.250.189.52;Port=5432;Database=dbwork;Username={txtUser.Text};Password={txtPass.Text};Include Error Detail=true";
                    using (var conn = new NpgsqlConnection(connString))
                    {
                        conn.Open();
                    }
                    form.Close();
                    InitMain();
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Ошибка входа: " + ex.Message);
                }
            };

            btnExit.Click += (s, e) => Application.Exit();

            form.Controls.AddRange(new Control[] { lblUser, txtUser, lblPass, txtPass, btnLogin, btnExit });
            form.ShowDialog();
        }

        private void InitMain()
        {
            this.Text = "Управление БД";
            this.Size = new Size(1100, 650);
            this.StartPosition = FormStartPosition.CenterScreen;

            topPanel.Dock = DockStyle.Top;
            topPanel.Height = 45;
            topPanel.ColumnCount = 8;
            topPanel.RowCount = 1;
            topPanel.Padding = new Padding(5);

            topPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 180));
            topPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 200));
            topPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 80));
            topPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 80));
            topPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 80));
            topPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 80));
            topPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 100));
            topPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));

            cbTables.DropDownStyle = ComboBoxStyle.DropDownList;
            cbTables.Dock = DockStyle.Fill;

            txtSearch.Dock = DockStyle.Fill;
            txtSearch.TextChanged += (s, e) => Search();

            var btnRefresh = new Button() { Text = "Обновить", Dock = DockStyle.Fill };
            btnRefresh.Click += (s, e) => LoadData();

            var btnAdd = new Button() { Text = "Добавить", Dock = DockStyle.Fill };
            btnAdd.Click += (s, e) => AddRecord();

            var btnEdit = new Button() { Text = "Редакт.", Dock = DockStyle.Fill };
            btnEdit.Click += (s, e) => EditRecord();

            var btnDelete = new Button() { Text = "Удалить", Dock = DockStyle.Fill };
            btnDelete.Click += (s, e) => DeleteRecord();

            var btnExport = new Button() { Text = "Экспорт CSV", Dock = DockStyle.Fill };
            btnExport.Click += (s, e) => ExportToCsv();

            lblStatus.Dock = DockStyle.Fill;
            lblStatus.TextAlign = ContentAlignment.MiddleLeft;

            topPanel.Controls.Add(cbTables, 0, 0);
            topPanel.Controls.Add(txtSearch, 1, 0);
            topPanel.Controls.Add(btnRefresh, 2, 0);
            topPanel.Controls.Add(btnAdd, 3, 0);
            topPanel.Controls.Add(btnEdit, 4, 0);
            topPanel.Controls.Add(btnDelete, 5, 0);
            topPanel.Controls.Add(btnExport, 6, 0);
            topPanel.Controls.Add(lblStatus, 7, 0);

            dgv.Dock = DockStyle.Fill;
            dgv.AllowUserToAddRows = false;
            dgv.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
            dgv.ReadOnly = true;
            dgv.DoubleClick += (s, e) => EditRecord();
            dgv.ClearSelection();

            this.Controls.Add(dgv);
            this.Controls.Add(topPanel);

            cbTables.SelectedIndexChanged += (s, e) => LoadData();

            LoadTables();
        }

        private void LoadTables()
        {
            try
            {
                var dt = ExecuteQuery($"SELECT table_name FROM information_schema.tables WHERE table_schema = '{schema}' AND table_type = 'BASE TABLE' ORDER BY table_name");
                cbTables.Items.Clear();
                foreach (DataRow row in dt.Rows)
                    cbTables.Items.Add(row["table_name"]);

                if (cbTables.Items.Count > 0)
                    cbTables.SelectedIndex = 0;
            }
            catch (Exception ex)
            {
                MessageBox.Show("Ошибка загрузки таблиц: " + ex.Message);
            }
        }

        private void LoadData()
        {
            if (cbTables.SelectedItem == null) return;
            try
            {
                string table = cbTables.SelectedItem.ToString();
                dgv.DataSource = ExecuteQuery($"SELECT * FROM {schema}.\"{table}\"");
                lblStatus.Text = $"Записей: {dgv.Rows.Count}";
                dgv.ClearSelection();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Ошибка загрузки данных: " + ex.Message);
            }
        }

        private void Search()
        {
            if (cbTables.SelectedItem == null || string.IsNullOrWhiteSpace(txtSearch.Text))
            {
                LoadData();
                return;
            }

            try
            {
                string table = cbTables.SelectedItem.ToString();
                string search = txtSearch.Text.Replace("'", "''");

                var cols = ExecuteQuery($@"
                    SELECT column_name FROM information_schema.columns 
                    WHERE table_schema='{schema}' AND table_name='{table}' 
                    AND data_type IN ('text', 'varchar', 'character varying')");

                string where = "";
                foreach (DataRow row in cols.Rows)
                    where += $"\"{row["column_name"]}\" ILIKE '%{search}%' OR ";

                if (where.Length > 0)
                    where = "WHERE " + where.Substring(0, where.Length - 4);

                dgv.DataSource = ExecuteQuery($"SELECT * FROM {schema}.\"{table}\" {where}");
                lblStatus.Text = $"Найдено: {dgv.Rows.Count}";
                dgv.ClearSelection();
            }
            catch (Exception)
            {
                LoadData();
            }
        }

        private void AddRecord()
        {
            string table = cbTables.SelectedItem.ToString();
            dgv.ClearSelection();
            var form = CreateEditForm(table, null);
            form.ShowDialog();
        }

        private void EditRecord()
        {
            if (dgv.SelectedRows.Count == 0)
            {
                MessageBox.Show("Выберите запись для редактирования", "Внимание",
                    MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            string table = cbTables.SelectedItem.ToString();
            var row = dgv.SelectedRows[0];
            var form = CreateEditForm(table, row);
            form.ShowDialog();
        }

        private void DeleteRecord()
        {
            if (dgv.SelectedRows.Count == 0)
            {
                MessageBox.Show("Выберите запись для удаления", "Внимание",
                    MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            string table = cbTables.SelectedItem.ToString();
            string idCol = dgv.Columns[0].Name;
            object idValue = dgv.SelectedRows[0].Cells[0].Value;

            if (MessageBox.Show($"Удалить запись ID={idValue}?", "Подтверждение",
                MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
                return;

            try
            {
                ExecuteNonQuery($"DELETE FROM {schema}.\"{table}\" WHERE \"{idCol}\" = {idValue}");
                LoadData();
                MessageBox.Show("Удалено!", "Успех", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            catch (Exception ex)
            {
                string msg = ex.Message;
                if (msg.Contains("foreign key") || msg.Contains("23503"))
                    msg = "Нельзя удалить! Есть связанные записи в других таблицах.";
                MessageBox.Show("Ошибка: " + msg, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void ExportToCsv()
        {
            if (dgv.Rows.Count == 0)
            {
                MessageBox.Show("Нет данных для экспорта", "Внимание", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            SaveFileDialog sfd = new SaveFileDialog();
            sfd.Filter = "CSV файлы|*.csv";
            sfd.DefaultExt = "csv";
            sfd.FileName = $"{cbTables.SelectedItem}_{DateTime.Now:yyyyMMdd_HHmmss}.csv";

            if (sfd.ShowDialog() != DialogResult.OK) return;

            try
            {
                using (var sw = new System.IO.StreamWriter(sfd.FileName, false, System.Text.Encoding.UTF8))
                {
                    for (int i = 0; i < dgv.Columns.Count; i++)
                    {
                        sw.Write($"\"{dgv.Columns[i].HeaderText}\"");
                        if (i < dgv.Columns.Count - 1) sw.Write(",");
                    }
                    sw.WriteLine();

                    foreach (DataGridViewRow row in dgv.Rows)
                    {
                        if (!row.IsNewRow)
                        {
                            for (int i = 0; i < dgv.Columns.Count; i++)
                            {
                                string val = row.Cells[i].Value?.ToString() ?? "";
                                sw.Write($"\"{val.Replace("\"", "\"\"")}\"");
                                if (i < dgv.Columns.Count - 1) sw.Write(",");
                            }
                            sw.WriteLine();
                        }
                    }
                }
                MessageBox.Show($"Экспорт завершён!\n{sfd.FileName}", "Успех", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Ошибка экспорта: {ex.Message}", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private Form CreateEditForm(string table, DataGridViewRow row)
        {
            var form = new Form();
            form.Text = row == null ? $"Добавление в {table}" : $"Редактирование {table}";
            form.Size = new Size(450, 500);
            form.StartPosition = FormStartPosition.CenterParent;
            form.FormBorderStyle = FormBorderStyle.FixedDialog;
            form.MinimizeBox = false;
            form.MaximizeBox = false;

            var panel = new Panel() { Dock = DockStyle.Fill, Padding = new Padding(10), AutoScroll = true };
            var fields = new Dictionary<string, TextBox>();

            var schemaDt = ExecuteQuery($@"
                SELECT column_name, data_type, is_identity, column_default
                FROM information_schema.columns 
                WHERE table_schema = '{schema}' AND table_name = '{table}'
                ORDER BY ordinal_position");

            string idCol = "";
            var pkDt = ExecuteQuery($@"
                SELECT kcu.column_name
                FROM information_schema.table_constraints tc
                JOIN information_schema.key_column_usage kcu 
                    ON tc.constraint_name = kcu.constraint_name
                WHERE tc.constraint_type = 'PRIMARY KEY' 
                    AND tc.table_schema = '{schema}'
                    AND tc.table_name = '{table}'");

            if (pkDt.Rows.Count > 0)
                idCol = pkDt.Rows[0]["column_name"].ToString();

            int y = 10;

            foreach (DataRow col in schemaDt.Rows)
            {
                string colName = col["column_name"].ToString();
                bool isIdentity = col["is_identity"].ToString() == "YES";
                bool hasDefault = !string.IsNullOrEmpty(col["column_default"].ToString());

                if (colName == idCol || isIdentity || hasDefault || colName.StartsWith("id_"))
                    continue;

                var lbl = new Label()
                {
                    Text = colName + ":",
                    Location = new Point(10, y),
                    Size = new Size(120, 25),
                    TextAlign = ContentAlignment.MiddleRight
                };

                var txt = new TextBox()
                {
                    Name = colName,
                    Location = new Point(140, y),
                    Size = new Size(260, 25)
                };

                if (row != null && row.Cells[colName].Value != null && row.Cells[colName].Value != DBNull.Value)
                    txt.Text = row.Cells[colName].Value.ToString();

                fields[colName] = txt;
                panel.Controls.Add(lbl);
                panel.Controls.Add(txt);
                y += 35;
            }

            if (fields.Count == 0)
            {
                var lblMsg = new Label()
                {
                    Text = "Нет полей для ввода\n(все поля заполняются автоматически)",
                    Location = new Point(10, 10),
                    Size = new Size(400, 50),
                    TextAlign = ContentAlignment.MiddleCenter
                };
                panel.Controls.Add(lblMsg);
                y = 60;
            }

            var btnSave = new Button() { Text = "Сохранить", Location = new Point(120, y + 10), Size = new Size(100, 35) };
            var btnCancel = new Button() { Text = "Отмена", Location = new Point(230, y + 10), Size = new Size(100, 35) };

            btnCancel.Click += (s, e) => form.Close();

            btnSave.Click += (s, e) =>
            {
                try
                {
                    if (row == null)
                    {
                        List<string> cols = new List<string>();
                        List<string> vals = new List<string>();

                        foreach (var f in fields)
                        {
                            string val = f.Value.Text.Trim();
                            if (!string.IsNullOrEmpty(val))
                            {
                                cols.Add($"\"{f.Key}\"");
                                vals.Add($"'{val.Replace("'", "''")}'");
                            }
                        }

                        if (cols.Count == 0)
                        {
                            MessageBox.Show("Заполните хотя бы одно поле", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
                            return;
                        }

                        string sql = $"INSERT INTO {schema}.\"{table}\" ({string.Join(", ", cols)}) VALUES ({string.Join(", ", vals)})";
                        ExecuteNonQuery(sql);
                    }
                    else
                    {
                        List<string> sets = new List<string>();
                        foreach (var f in fields)
                        {
                            string val = f.Value.Text.Trim();
                            sets.Add($"\"{f.Key}\" = '{val.Replace("'", "''")}'");
                        }

                        string sql = $"UPDATE {schema}.\"{table}\" SET {string.Join(", ", sets)} WHERE \"{idCol}\" = {row.Cells[idCol].Value}";
                        ExecuteNonQuery(sql);
                    }

                    form.Close();
                    LoadData();
                    MessageBox.Show("Сохранено!", "Успех", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
                catch (Exception ex)
                {
                    string msg = ex.Message;
                    if (msg.Contains("duplicate key") || msg.Contains("23505"))
                        msg = "Такая запись уже существует! Введите другое значение.";
                    else if (msg.Contains("not null") || msg.Contains("23502"))
                        msg = "Заполните все поля!";
                    else if (msg.Contains("foreign key") || msg.Contains("23503"))
                        msg = "Ошибка: проверьте что ссылаетесь на существующую запись.";

                    MessageBox.Show("Ошибка: " + msg, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            };

            panel.Controls.Add(btnSave);
            panel.Controls.Add(btnCancel);
            form.Controls.Add(panel);

            return form;
        }

        private DataTable ExecuteQuery(string sql)
        {
            var dt = new DataTable();
            using (var conn = new NpgsqlConnection(connString))
            using (var cmd = new NpgsqlCommand(sql, conn))
            using (var da = new NpgsqlDataAdapter(cmd))
            {
                conn.Open();
                da.Fill(dt);
            }
            return dt;
        }

        private void ExecuteNonQuery(string sql)
        {
            using (var conn = new NpgsqlConnection(connString))
            using (var cmd = new NpgsqlCommand(sql, conn))
            {
                conn.Open();
                cmd.ExecuteNonQuery();
            }
        }
    }
}
