import logger from "./logger"; // Assuming logger is imported from a separate file
import * as fs from "fs";
import * as path from "path";
import { quoteColumn as q } from "./helpers";

import type { Database } from "sqlite3";

// Interface for a Query object (optional for improved type safety)
interface Query {
  id: number;
  name: string;
  sqlstatement: string;
}

// Interface for a ColumnInfo object (optional for improved type safety)
interface ColumnInfo {
  field: string;
  type: string;
  fk?: any[];
}

// Define the type for the foreign key information
interface ForeignKeyInfo {
  table: string;
  from: string;
  to: string;
}

// Define the return type for the fetchTableForeignKeys function
interface FetchTableForeignKeysResult {
  bool: boolean;
  data?: ForeignKeyInfo[];
  error?: string;
}

interface Table {
  name: string;
  sql: string;
}

interface Column {
  cid: number;
  name: string;
  type: string;
  notnull: number;
  dflt_value: any;
  pk: number;
}

interface Row {
  [key: string]: any;
}

async function InitializeDB(db: Database): Promise<void> {
  return new Promise((resolve, reject) => {
    db.serialize(() => {
      db.get(
        "SELECT name FROM sqlite_master WHERE type='table' AND name='query'",
        (err, row) => {
          if (err) {
            logger.error("Error checking for query:", err.message);
            reject(err);
            return;
          }
          if (!row || row === undefined) {
            db.run(
              `
              CREATE TABLE IF NOT EXISTS query (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                name TEXT,
                sqlstatement TEXT
              )
            `,
              (err) => {
                if (err) {
                  logger.error("Error creating query:", err.message);
                  reject(err);
                  return;
                }
                resolve(); // No error, table created successfully
              }
            );
          } else {
            resolve(); // No error, table already exists
          }
        }
      );
    });
  });
}

function insertQuery(
  db: Database,
  name: string,
  sqlStatement: string
): Promise<{ bool: boolean; error?: string }> {
  return new Promise((resolve, reject) => {
    db.run(
      `INSERT INTO query (name, sqlstatement) VALUES (?, ?)`,
      [name, sqlStatement],
      (error) => {
        if (error) {
          logger.error("SQL Statement : " + sqlStatement);
          logger.error(error.message);
          reject({ bool: false, error: error.message });
          return;
        } else {
          resolve({ bool: true });
        }
      }
    );
  });
}

function fetchQueries(
  db: Database
): Promise<{ bool: boolean; data?: Query[] }> {
  return new Promise((resolve, reject) => {
    db.all(`SELECT * FROM query`, function (error, rows: any) {
      if (error) {
        logger.error("Error while fetching table");
        logger.error(error.message);
        reject({ bool: false, error: error.message });
        return;
      } else {
        resolve({ bool: true, data: rows });
      }
    });
  });
}

function fetchAllTables(
  db: Database
): Promise<{ bool: boolean; data?: string[] }> {
  return new Promise((resolve, reject) => {
    db.all(
      "SELECT name FROM sqlite_master WHERE type='table'",
      function (error, rows: any) {
        if (error) {
          logger.error("Error while fetching tables");
          logger.error(error.message);
          reject({ bool: false, error: error.message });
          return;
        } else {
          resolve({ bool: true, data: rows });
        }
      }
    );
  });
}

function fetchTable(
  db: Database,
  table: string
): Promise<{ bool: boolean; data?: any[] }> {
  return new Promise((resolve, reject) => {
    db.all(`SELECT * FROM ${q(table)}`, function (error, rows) {
      if (error) {
        logger.error("Error while fetching table");
        logger.error(error.message);
        reject({ bool: false, error: error.message });
        return;
      } else {
        resolve({ bool: true, data: rows });
      }
    });
  });
}

function fetchRecord(
  db: Database,
  table: string,
  label: string,
  id: number | string
): Promise<{ bool: boolean; data?: any[] }> {
  return new Promise((resolve, reject) => {
    db.all(
      `SELECT * FROM ${q(table)} WHERE ${label} = ${
        typeof id === "string" ? `'${id}'` : id
      }`,
      function (error, rows: any) {
        if (error) {
          logger.error("Error while fetching record");
          logger.error(error.message);
          reject({ bool: false, error: error.message });
          return;
        } else {
          resolve({ bool: true, data: rows });
        }
      }
    );
  });
}

function fetchTableInfo(
  db: Database,
  table: string
): Promise<{ bool: boolean; data?: ColumnInfo[] }> {
  return new Promise((resolve, reject) => {
    db.all(`PRAGMA table_info(${q(table)})`, function (error, rows: any) {
      if (error) {
        logger.error("Error while fetching table info");
        logger.error(error.message);
        reject({ bool: false, error: error.message });
      } else {
        // Filter out the auto-increment column
        // const filteredColumns = rows.filter(
        //   (row: any) =>
        //     !(
        //       (row.pk > 0 && row.type.toUpperCase() === "INTEGER") ||
        //       row.type.toUpperCase() === "DATETIME"
        //     )
        // );

        // Map the remaining columns to an array of column objects with name and type
        const tableInfo = rows.map((row: any) => ({
          field: row.name,
          type: row.type,
        }));

        if (tableInfo.length === 0) {
          reject({
            bool: false,
            error: "No columns to select (all columns are auto-increment).",
          });
        } else {
          resolve({ bool: true, data: tableInfo });
        }
      }
    });
  });
}

function fetchAllTableInfo(
  db: Database,
  table: string
): Promise<{ bool: boolean; data?: ColumnInfo[] }> {
  return new Promise((resolve, reject) => {
    db.all(`PRAGMA table_info(${q(table)})`, function (error, rows: any) {
      if (error) {
        logger.error("Error while fetching table info");
        logger.error(error.message);
        reject({ bool: false, error: error.message });
      } else {
        // Map the columns to an array of column objects with name and type
        const tableInfo = rows.map((row: any) => ({
          field: row.name,
          type: row.type,
        }));

        if (tableInfo.length === 0) {
          reject({
            bool: false,
            error: "No columns to select (all columns are auto-increment).",
          });
        } else {
          resolve({ bool: true, data: tableInfo });
        }
      }
    });
  });
}

// Function to fetch foreign key info of a table
async function fetchTableForeignKeys(
  db: Database,
  table: string
): Promise<FetchTableForeignKeysResult> {
  return new Promise((resolve, reject) => {
    db.all(`PRAGMA foreign_key_list(${q(table)})`, function (error, rows: any) {
      if (error) {
        console.error("Error while fetching foreign key info");
        console.error(error.message);
        reject({ bool: false, error: error.message });
      } else {
        if (rows.length === 0) {
          resolve({ bool: false, data: [] });
        } else {
          const foreignKeys = rows.map((row: any) => ({
            table: row.table,
            from: row.from,
            to: row.to,
          }));

          resolve({ bool: true, data: foreignKeys });
        }
      }
    });
  });
}

function fetchFK(
  db: Database,
  table: string,
  column: string
): Promise<{ bool: boolean; data: any[] }> {
  return new Promise((resolve, reject) => {
    db.all(`SELECT ${q(column)} from ${q(table)}`, function (error, rows) {
      if (error) {
        logger.error("Error while fetching Foreign keys");
        logger.error(error.message);
        reject({ bool: false, data: [], error: error.message });
        return;
      } else {
        resolve({ bool: true, data: rows });
      }
    });
  });
}

function runQuery(
  db: Database,
  sqlStatement: string
): Promise<{ bool: boolean; error?: string }> {
  return new Promise((resolve, reject) => {
    db.run(sqlStatement, function (error) {
      if (error) {
        logger.error("SQL Statement : " + sqlStatement);
        logger.error(error.message);
        reject({ bool: false, error: error.message });
        return;
      } else {
        resolve({ bool: true });
      }
    });
  });
}

function runSelectQuery(
  db: Database,
  sqlStatement: string
): Promise<{ bool: boolean; data?: any[] }> {
  return new Promise((resolve, reject) => {
    db.all(sqlStatement, function (error, rows) {
      if (error) {
        logger.error("SQL Statement : " + sqlStatement);
        logger.error(error.message);
        reject({ bool: false, error: error.message });
        return;
      } else {
        resolve({ bool: true, data: rows });
      }
    });
  });
}

function checkColumnHasDefault(
  db: Database,
  tableName: string,
  columnType: string,
  columnName: string
): Promise<{ bool: boolean; message?: string; error?: string }> {
  return new Promise((resolve, reject) => {
    try {
      // Construct the SQL query to check if the column has a default value
      const sql = `
                SELECT sql
                FROM sqlite_master
                WHERE type = 'table' AND name = '${q(tableName)}'
            `;

      // Execute the SQL query
      db.get(sql, [], (err, row: any) => {
        if (err) {
          reject({
            bool: false,
            message: "Error while executing query",
            error: err.message,
          });
          return;
        }

        if (!row) {
          resolve({ bool: false, message: "Table not found" });
          return;
        }
        // Check if the SQL definition contains the column name and the word "DEFAULT"
        const hasDefault = row.sql.includes(
          `${q(columnName)} ${columnType} DEFAULT`
        );

        // Return the result
        resolve({ bool: hasDefault, message: "" });
      });
    } catch (error: any) {
      reject({
        bool: false,
        message: "Error while executing query",
        error: error.message,
      });
    }
  });
}

function deleteFromTable(
  db: Database,
  name: string,
  id: number | string
): Promise<{ bool: boolean; error?: string }> {
  // console.log(sqlStatement);
  return new Promise((resolve, reject) => {
    db.run(
      `DELETE FROM ${name} WHERE id = ${
        typeof id === "string" ? `'${id}'` : id
      };`,
      function (error) {
        if (error) {
          logger.error("Error while deleting");
          logger.error(error.message);
          reject({ bool: false, error: error.message });
          return;
        } else {
          resolve({ bool: true });
        }
      }
    );
  });
}

function exportDatabaseToSQL(
  db: Database
): Promise<{ bool: boolean; filePath?: string; error?: string }> {
  return new Promise((resolve, reject) => {
    const outputPath = path.resolve(
      __dirname,
      "..",
      "..",
      "public",
      "output.sql"
    );
    let sql = "";

    db.serialize(() => {
      db.all(
        "SELECT name, sql FROM sqlite_master WHERE type='table'",
        (err, tables: Table[]) => {
          if (err) {
            reject({ bool: false, error: err.message });
            return;
          }

          let pendingTables = tables.length;

          tables.forEach((table) => {
            const tableName = table.name;
            const createTableSQL = table.sql;

            sql += `-- Dumping data for table ${q(tableName)}\n`;
            sql += `${createTableSQL};\n`;

            db.all(
              `PRAGMA table_info(${q(tableName)})`,
              (err, columns: Column[]) => {
                if (err) {
                  reject({ bool: false, error: err.message });
                  return;
                }

                columns.forEach((column) => {
                  sql += `-- ${column.cid} | ${column.name} | ${column.type} | ${column.notnull} | ${column.dflt_value} | ${column.pk}\n`;
                });

                db.all(`SELECT * FROM ${q(tableName)}`, (err, rows: Row[]) => {
                  if (err) {
                    reject({ bool: false, error: err.message });
                    return;
                  }

                  rows.forEach((row) => {
                    const columns = Object.keys(row).map(q).join(", ");
                    const values = Object.values(row)
                      .map((value) => `'${value}'`)
                      .join(", ");
                    sql += `INSERT INTO ${q(
                      tableName
                    )} (${columns}) VALUES (${values});\n`;
                  });

                  pendingTables -= 1;

                  if (pendingTables === 0) {
                    fs.writeFile(outputPath, sql, (err) => {
                      if (err) {
                        reject({ bool: false, error: err.message });
                        return;
                      }

                      resolve({ bool: true, filePath: outputPath });
                    });
                  }
                });
              }
            );
          });
        }
      );
    });
  });
}

export default {
  checkColumnHasDefault,
  fetchAllTables,
  fetchTable,
  fetchTableInfo,
  fetchAllTableInfo,
  deleteFromTable,
  fetchRecord,
  runQuery,
  runSelectQuery,
  InitializeDB,
  insertQuery,
  fetchQueries,
  fetchTableForeignKeys,
  fetchFK,
  exportDatabaseToSQL,
};
