import 'dart:io';

import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:flutter/foundation.dart';
import 'package:niogu_app/core/database/daos/campaign_dao.dart';
import 'package:niogu_app/core/database/daos/category_dao.dart';
import 'package:niogu_app/core/database/daos/customer_address_dao.dart';
import 'package:niogu_app/core/database/daos/customer_dao.dart';
import 'package:niogu_app/core/database/daos/order_dao.dart';
import 'package:niogu_app/core/database/daos/order_item_dao.dart';
import 'package:niogu_app/core/database/daos/outlet_campaign_dao.dart';
import 'package:niogu_app/core/database/daos/outlet_dao.dart';
import 'package:niogu_app/core/database/daos/outlet_inventory_dao.dart';
import 'package:niogu_app/core/database/daos/product_dao.dart';
import 'package:niogu_app/core/database/daos/product_like_dao.dart';
import 'package:niogu_app/core/database/daos/product_review_dao.dart';
import 'package:niogu_app/core/database/daos/product_variant_dao.dart';
import 'package:niogu_app/core/database/daos/purchase_dao.dart';
import 'package:niogu_app/core/database/daos/purchase_item_dao.dart';
import 'package:niogu_app/core/database/daos/raw_material_dao.dart';
import 'package:niogu_app/core/database/daos/stock_card_dao.dart';
import 'package:niogu_app/core/database/daos/supplier_dao.dart';
import 'package:niogu_app/core/database/daos/sync_queue_dao.dart';
import 'package:niogu_app/core/database/daos/tenant_dao.dart';
import 'package:niogu_app/core/database/daos/tenant_payment_method_dao.dart';
import 'package:niogu_app/core/database/daos/transaction_dao.dart';
import 'package:niogu_app/core/database/daos/unit_dao.dart';
import 'package:niogu_app/core/database/daos/receipt_item_dao.dart';
import 'package:niogu_app/core/database/daos/user_dao.dart';
import 'package:niogu_app/core/database/tables/campaigns.dart';
import 'package:niogu_app/core/database/tables/categories.dart';
import 'package:niogu_app/core/database/tables/customer_addresses.dart';
import 'package:niogu_app/core/database/tables/customers.dart';
import 'package:niogu_app/core/database/tables/order_items.dart';
import 'package:niogu_app/core/database/tables/orders.dart';
import 'package:niogu_app/core/database/tables/outlet_campaigns.dart';
import 'package:niogu_app/core/database/tables/outlet_inventories.dart';
import 'package:niogu_app/core/database/tables/outlets.dart';
import 'package:niogu_app/core/database/tables/product_likes.dart';
import 'package:niogu_app/core/database/tables/product_reviews.dart';
import 'package:niogu_app/core/database/tables/product_variants.dart';
import 'package:niogu_app/core/database/tables/products.dart';
import 'package:niogu_app/core/database/tables/purchase_items.dart';
import 'package:niogu_app/core/database/tables/purchases.dart';
import 'package:niogu_app/core/database/tables/raw_materials.dart';
import 'package:niogu_app/core/database/tables/receipt_items.dart';
import 'package:niogu_app/core/database/tables/stock_cards.dart';
import 'package:niogu_app/core/database/tables/suppliers.dart';
import 'package:niogu_app/core/database/tables/sync_queues.dart';
import 'package:niogu_app/core/database/tables/tenant_payment_methods.dart';
import 'package:niogu_app/core/database/tables/tenants.dart';
import 'package:niogu_app/core/database/tables/transactions.dart';
import 'package:niogu_app/core/database/tables/units.dart';
import 'package:niogu_app/core/database/tables/users.dart';
import 'package:niogu_app/core/system/system_secure.dart';
import 'package:niogu_app/core/utils/string_list_converter.dart';
import 'package:niogu_app/core/utils/string_map_converter.dart';
import 'package:niogu_app/core/system/system_setting.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
import 'package:uuid/uuid.dart';

part 'app_database.g.dart';

@DriftDatabase(
  tables: [
    Tenants,
    TenantPaymentMethods,
    Users,
    Customers,
    CustomerAddresses,
    Outlets,
    Categories,
    Units,
    Products,
    ProductVariants,
    ProductReviews,
    ProductLikes,
    RawMaterials,
    OutletInventories,
    ReceiptItems,
    StockCards,
    Orders,
    OrderItems,
    Purchases,
    PurchaseItems,
    Suppliers,
    Transactions,
    Campaigns,
    OutletCampaigns,
    SyncQueues
  ],
  daos: [
    TenantDao,
    TenantPaymentMethodDao,
    UserDao,
    CustomerDao,
    CustomerAddressDao,
    OutletDao,
    CategoryDao,
    UnitDao,
    ProductDao,
    ProductVariantDao,
    ProductReviewDao,
    ProductLikeDao,
    RawMaterialDao,
    OutletInventoryDao,
    ReceiptItemDao,
    StockCardDao,
    OrderDao,
    OrderItemDao,
    SupplierDao,
    PurchaseDao,
    PurchaseItemDao,
    TransactionDao,
    CampaignDao,
    OutletCampaignDao,
    SyncQueueDao
  ],
)
class AppDatabase extends _$AppDatabase {
  AppDatabase() : super(_openConnection());

  @override
  // TODO: implement schemaVersion
  int get schemaVersion => 1;

  TenantDao get tenantDao => TenantDao(this);
  TenantPaymentMethodDao get tenantPaymentMethodDao =>
      TenantPaymentMethodDao(this);
  UserDao get userDao => UserDao(this);
  CustomerDao get customerDao => CustomerDao(this);
  CustomerAddressDao get customerAddressDao => CustomerAddressDao(this);
  OutletDao get outletDao => OutletDao(this);
  CategoryDao get categoryDao => CategoryDao(this);
  UnitDao get unitDao => UnitDao(this);
  ProductDao get productDao => ProductDao(this);
  ProductVariantDao get productVariantDao => ProductVariantDao(this);
  ProductReviewDao get productReviewDao => ProductReviewDao(this);
  ProductLikeDao get productLikeDao => ProductLikeDao(this);
  RawMaterialDao get rawMaterialDao => RawMaterialDao(this);
  OutletInventoryDao get outletInventoryDao => OutletInventoryDao(this);
  ReceiptItemDao get receiptItemDao => ReceiptItemDao(this);
  StockCardDao get stockCardDao => StockCardDao(this);
  OrderDao get orderDao => OrderDao(this);
  OrderItemDao get orderItemDao => OrderItemDao(this);
  PurchaseDao get purchaseDao => PurchaseDao(this);
  PurchaseItemDao get purchaseItemDao => PurchaseItemDao(this);
  SupplierDao get supplierDao => SupplierDao(this);
  CampaignDao get campaignDao => CampaignDao(this);
  OutletCampaignDao get outletCampaignDao => OutletCampaignDao(this);
  SyncQueueDao get syncQueueDao => SyncQueueDao(this);

  @override
  // TODO: implement migration
  MigrationStrategy get migration => MigrationStrategy(
    onCreate: (m) async {
      await m.createAll();

      await customStatement(
        'CREATE UNIQUE INDEX outlet_email_unique_active ON outlets(email) WHERE deleted_at IS NULL',
      );

      await customStatement(
        'CREATE UNIQUE INDEX outlet_phone_unique_active ON outlets(phone_number) WHERE deleted_at IS NULL',
      );

      await customStatement(
        'CREATE UNIQUE INDEX users_email_unique_active ON users(email) WHERE deleted_at IS NULL',
      );

      await customStatement(
        'CREATE UNIQUE INDEX users_phone_unique_active ON users(phone_number) WHERE deleted_at IS NULL',
      );

      await customStatement(
        'CREATE INDEX IF NOT EXISTS idx_products_created_at ON products (created_at)',
      );

      await customStatement(
        'CREATE INDEX IF NOT EXISTS idx_products_deleted_at ON products (deleted_at)',
      );

      await customStatement(
        'CREATE INDEX IF NOT EXISTS idx_outlet_inventories_lookup ON outlet_inventories (item_type, item_id)',
      );

      await customStatement(
        'CREATE INDEX IF NOT EXISTS idx_outlet_inventories_deleted_at ON outlet_inventories (deleted_at)',
      );

      await customStatement(
        'CREATE INDEX IF NOT EXISTS idx_transaction_lookup ON transactions (reference_type, reference_id)',
      );
    },
    onUpgrade: (m, from, to) async {
      // Handle database upgrades if needed
    },
  );
}

LazyDatabase _openConnection() {
  return LazyDatabase(() async {
    final tenantCode = await SystemSetting.getTenantCode();

    final dbFolder = await getApplicationDocumentsDirectory();

    final file = File(path.join(dbFolder.path, 'NIOGU_$tenantCode.sqlite'));

    var databaseKey = await SystemSecure.getDatabaseKey(tenantCode!);

    if (databaseKey == null) {
      await SystemSecure.generateDatabaseKey(tenantCode);

      databaseKey = await SystemSecure.getDatabaseKey(tenantCode);
    }

    return NativeDatabase.createInBackground(
      file,
      setup: (database) {
        database.execute('PRAGMA foreign_keys = ON');
        database.execute("PRAGMA key = '$databaseKey';");
      },
      logStatements: kDebugMode,
    );
  });
}
