################################################################################
# Note
# 2025-04-12 new
#
# SQL標準により下記が原則だが、ダブルクオートが識別子にヒットしないときには
# ダブルクオートでも文字列として解釈される。
#     ダブルクオート " は識別子（列名など）を表すのに使われる
#     シングルクオート ' は文字列を表すのに使われる
#
# 下記は DataFrame へのデータ読み込みもやや遅くなる
#     con.row_factory = sqlite3.Row  # 辞書型で結果を受け取る
################################################################################
import os
import sqlite3
import pandas as pd


def open(dbfilename, timeout=5, create=False):
    ''' DBファイルを開く
    Args:
      dbfilename (str): ファイル名
      timeout (float): タイムアウト [s]
      create (bool): ファイルが存在しないときに新規作成するかどうか

    Returns:
      SQLite3_Database: データベースクラス
    '''
    return SQLite3_Database(dbfilename, timeout, create)


class SQLite3_Database:
    def __init__(self, dbfilename, timeout, create=None):
        if (not create) and (not os.path.exists(dbfilename)):
            # 新規作成せずファイルが存在しないときエラー
            raise Exception(f'File "{dbfilename}" does not exist')

        # DBに接続（ファイルが存在しないときは新規作成される）
        #     detect_types=sqlite3.PARSE_DECLTYPES: DBのデータ型を利用して自動変換する
        self.con = sqlite3.connect(
            dbfilename,
            detect_types=sqlite3.PARSE_DECLTYPES,
            timeout=timeout)
        self.con.execute("PRAGMA foreign_keys = 1")  # 外部キー制約を有効にする

        self.filename = dbfilename

    def get_table_names(self):
        sql = "SELECT name FROM sqlite_master WHERE type='table'"
        cur = self.con.execute(sql)
        return [x[0] for x in cur]

    def get_column_names(self, tablename):
        tablenames = self.get_table_names()
        if tablename not in tablenames:
            raise Exception(f'tablename "{tablename}" does not exist')

        sql = f'SELECT * FROM "{tablename}"'
        cur = self.con.execute(sql)
        return [x[0] for x in cur.description]

    def get_table_info(self, tablename):
        sql = f"SELECT * FROM pragma_table_info('{tablename}')"
        return pd.read_sql_query(sql, self.con)

    def get_range(self, tablename, colname):
        sql = f'SELECT MIN("{colname}"), MAX("{colname}") FROM "{tablename}"'
        cur = self.con.execute(sql)
        return cur.fetchone()

    def count(self, tablename):
        sql = f'SELECT COUNT(*) FROM "{tablename}"'
        cur = self.con.execute(sql)
        return cur.fetchone()[0]