import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:gifunavi/model/destination.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

import '../model/rog.dart';

class DatabaseHelper {
  DatabaseHelper._privateConstructor();
  static final DatabaseHelper instance = DatabaseHelper._privateConstructor();

  static Database? _database;
  Future<Database> get database async => _database ??= await _initDatabase();

  // データベース初期化:
  //
  // シングルトンパターンを使用してDatabaseHelperのインスタンスを管理しています。
  // _initDatabase()メソッドでデータベースを初期化し、必要なテーブルを作成します。
  //
  Future<Database> _initDatabase() async {
    Directory documentDirectory = await getApplicationDocumentsDirectory();
    String path = join(documentDirectory.path, 'rog.db');
    // return  await openDatabase(
    //   path,
    //   version: 1,
    //   onCreate: _onCreate,
    // );
    return openDatabase(
        join(
          await getDatabasesPath(),
          'rog.db',
        ),
        version: 1,
        onCreate: _onCreate);
  }

  // DBを初期化する際に、必要なテーブルを作成します。
  //
  Future _onCreate(Database db, int version) async {
    // destinationテーブル: 目的地の情報を保存（位置、名前、住所、連絡先情報など）。
    await db.execute('''
    CREATE TABLE destination(
      location_id INTEGER PRIMARY KEY,
      name TEXT,
      address TEXT,
      phone TEXT,
      email TEXT,
      webcontents TEXT,
      videos TEXT,
      category TEXT,
      series INTEGER,
      lat REAL, 
      lon REAL, 
      list_order INTEGER,
      photos TEXT,
      checkin_radious REAL,
      sub_loc_id TEXT,
      auto_checkin INTEGER,
      selected INTEGER,
      checkedin INTEGER,
      cp REAL,
      checkin_point REAL,
      buy_point REAL,
      hidden_location INTEGER,
      checkin_image TEXT,
      buypoint_image TEXT,
      forced_checkin INTEGER,
      recipt_times INTEGER,
      tags TEXT
    )
    ''');

    // rogainingテーブル: ロゲイニング（orienteering的なアクティビティ）の記録を保存。
    await db.execute('''
    CREATE TABLE rogaining(
      rog_id INTEGER PRIMARY KEY AUTOINCREMENT,
      course_id INTEGER,
      location_id INTEGER,
      user_id INTEGER,
      lat REAL,
      lon REAL,
      time_stamp INTEGER,
      image TEXT
    )
    ''');

    // rogテーブル: ロゲイニングのチェックポイント情報を保存。
    await db.execute('''
    CREATE TABLE rog(
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      team_name TEXT,
      event_code TEXT,
      user_id INTEGER,
      cp_number INTEGER,
      checkintime INTEGER,
      image TEXT,
      rog_action_type INTEGER
    )
    ''');
  }

  Future<List<Rog>> allRogianing() async {
    Database db = await instance.database;
    var rog = await db.query('rog');
    List<Rog> roglist =
        rog.isNotEmpty ? rog.map((e) => Rog.fromMap(e)).toList() : [];
    //print("--------- $rog");
    return roglist;
  }

  Future<List<Rog>> getRogainingByLatLon(double lat, double lon) async {
    Database db = await instance.database;
    var rog = await db.query('rog', where: "lat = $lat and lon= $lon");
    List<Rog> roglist =
        rog.isNotEmpty ? rog.map((e) => Rog.fromMap(e)).toList() : [];
    return roglist;
  }

  Future clearSelection() async {
    Database db = await instance.database;
    Map<String, dynamic> rowClear = {"selected": false};
    return await db.update("destination", rowClear);
  }

  Future<int> toggleSelecttion(Destination dest) async {
    Database db = await instance.database;

    bool val = !dest.selected!;
    Map<String, dynamic> rowTarget = {"selected": val};

    await clearSelection();

    return await db.update("destination", rowTarget,
        where: 'location_id = ?', whereArgs: [dest.location_id!]);
  }

  Future<int> deleteRogaining(int id) async {
    Database db = await instance.database;
    var rog = await db.delete('rog', where: "id = $id");
    int ret = rog > 0 ? rog : -1;

    return ret;
  }

  Future<void> deleteAllRogaining() async {
    Database db = await instance.database;
    await db.delete('rog');
  }

  Future<void> deleteAllRogainingExceptToday() async {
    Database db = await instance.database;

    // 今日の開始時刻をエポックミリ秒で取得
    final now = DateTime.now();
    final startOfDay = DateTime(now.year, now.month, now.day).millisecondsSinceEpoch;

    // 今日チェックインしたもの以外を削除
    await db.delete(
        'rog',
        where: 'checkintime < ?',
        whereArgs: [startOfDay]
    );
  }


  Future<bool> isRogAlreadyAvailable(int id) async {
    Database db = await instance.database;
    var rog = await db.query('rog', where: "id = $id");
    return rog.isNotEmpty ? true : false;
  }

  Future<int?> latestGoal() async {
    Database db = await instance.database;
    return Sqflite.firstIntValue(
        await db.rawQuery('SELECT MAX(checkintime) FROM rog'));
  }

  Future<int> insertRogaining(Rog rog) async {
    Database db = await instance.database;
    int? nextOrder =
        Sqflite.firstIntValue(await db.rawQuery('SELECT MAX(id) FROM rog'));
    nextOrder = nextOrder ?? 0;
    nextOrder = nextOrder + 1;
    rog.id = nextOrder;
    int res = await db.insert(
      'rog',
      rog.toMap(),
      conflictAlgorithm: ConflictAlgorithm.replace,
    );
    //print("------ database helper insert $res-----------::::::::");
    return res;
  }

  Future<List<Destination>> getDestinations() async {
    Database db = await instance.database;
    var dest = await db.query('destination', orderBy: 'list_order');
    List<Destination> destList =
        dest.isNotEmpty ? dest.map((e) => Destination.fromMap(e)).toList() : [];
    //print("--------- $destList");
    return destList;
  }

  Future<List<Destination>> getDestinationById(int id) async {
    Database db = await instance.database;
    var rog = await db.query('destination', where: "location_id = $id");
    List<Destination> deslist =
        rog.isNotEmpty ? rog.map((e) => Destination.fromMap(e)).toList() : [];
    return deslist;
  }

  Future<List<Destination>> getDestinationByLatLon(
      double lat, double lon) async {
    Database db = await instance.database;
    var dest = await db.query('destination',
        where: "lat = $lat and lon= $lon", orderBy: 'list_order');
    List<Destination> destList =
        dest.isNotEmpty ? dest.map((e) => Destination.fromMap(e)).toList() : [];
    return destList;
  }

  Future<int> deleteDestination(int locationId) async {
    Database db = await instance.database;
    var dest =
        await db.delete('destination', where: "location_id = $locationId");
    int ret = dest > 0 ? dest : -1;

    //after deleting set correct order
    await setOrder();

    return ret;
  }

  Future<void> setOrder() async {
    Database db = await instance.database;
    var byOrder = await db.query('destination', orderBy: 'list_order');

    List<Destination> desDb = byOrder.isNotEmpty
        ? byOrder.map((e) => Destination.fromMap(e)).toList()
        : [];

    int order = 1;
    for (Destination d in desDb) {
      Map<String, dynamic> rowTarget = {"list_order": order};

      await db.update("destination", rowTarget,
          where: 'location_id = ?', whereArgs: [d.location_id]);

      order += 1;
    }
  }

  Future<void> deleteAllDestinations() async {
    Database db = await instance.database;
    await db.delete('destination');
  }

  Future<void> deleteAllDestinationsExceptTodayCheckins() async {
    Database db = await instance.database;

    // 今日の開始時刻をエポックからのミリ秒で取得
    final now = DateTime.now();
    final startOfDay = DateTime(now.year, now.month, now.day).millisecondsSinceEpoch;

    // 今日チェックインされ、buy_pointを持つ目的地を除いて全て削除
    await db.rawDelete('''
      DELETE FROM destination 
      WHERE location_id NOT IN (
        SELECT d.location_id 
        FROM destination d
        JOIN rog r ON d.location_id = r.cp_number
        WHERE date(r.checkintime / 1000, 'unixepoch', 'localtime') = date('now', 'localtime') 
          AND d.buy_point > 0 
          AND d.checkedin = 1
      )
    ''', [startOfDay]);
  }

  Future<bool> isAlreadyAvailable(int locationId) async {
    Database db = await instance.database;
    var dest =
        await db.query('destination', where: "location_id = $locationId");
    return dest.isNotEmpty ? true : false;
  }

  Future<int> insertDestination(Destination dest) async {
    await deleteDestination(dest.location_id!);
    Database db = await instance.database;
    int? nextOrder = Sqflite.firstIntValue(
        await db.rawQuery('SELECT MAX(list_order) FROM destination'));
    nextOrder = nextOrder ?? 0;
    nextOrder = nextOrder + 1;
    dest.list_order = nextOrder;
    int res = await db.insert(
      'destination',
      dest.toMap(),
      conflictAlgorithm: ConflictAlgorithm.replace,
    );
    //print("------ database helper insert ${dest.toMap()}-----------::::::::");
    return res;
  }

  Future<int> updateCancelBuyPoint(Destination destination) async {
    //print("---- updating puypint image in db -----");
    Database db = await instance.database;
    Map<String, dynamic> row = {"buy_point": 0};
    return await db.update("destination", row,
        where: 'location_id = ?', whereArgs: [destination.location_id!]);
  }

  Future<int> updateBuyPoint(Destination destination, String imageUrl) async {
    //print("---- updating puypint image in db -----");
    Database db = await instance.database;
    Map<String, dynamic> row = {"buypoint_image": imageUrl};
    return await db.update("destination", row,
        where: 'location_id = ?', whereArgs: [destination.location_id!]);
  }

  Future<int> updateAction(Destination destination, bool checkin) async {
    Database db = await instance.database;
    int act = checkin == false ? 0 : 1;
    Map<String, dynamic> row = {"checkedin": act};
    return await db.update("destination", row,
        where: 'location_id = ?', whereArgs: [destination.location_id!]);
  }

  Future<void> updateOrder(Destination d, int dir) async {
    Database db = await instance.database;
    var target = await db.query('destination',
        where: "list_order = ${d.list_order! + dir}");
    var dest =
        await db.query('destination', where: "location_id = ${d.location_id}");

    // print("--- target in db is $target");
    // print("--- destine in db is $dest");

    if (target.isNotEmpty) {
      List<Destination> targetIndb = target.isNotEmpty
          ? target.map((e) => Destination.fromMap(e)).toList()
          : [];

      List<Destination> destIndb = dest.isNotEmpty
          ? dest.map((e) => Destination.fromMap(e)).toList()
          : [];

      Map<String, dynamic> rowTarget = {"list_order": d.list_order};

      Map<String, dynamic> rowDes = {
        "list_order": destIndb[0].list_order! + dir
      };

      // print("--- target destination is ${target_indb[0].location_id}");
      // print("--- destine destination is is ${dest_indb[0].location_id}");

      await db.update("destination", rowTarget,
          where: 'location_id = ?', whereArgs: [targetIndb[0].location_id]);

      await db.update("destination", rowDes,
          where: 'location_id = ?', whereArgs: [destIndb[0].location_id]);
    }
  }

  // Future<int?> getPending() async{
  //   Database db = await instance.database;
  //   return await Sqflite.firstIntValue(await db.rawQuery("SELECT COUNT(*) FROM incidents"));
  // }
}
