import 'package:drift/drift.dart';
import 'package:niogu_app/core/database/app_database.dart' hide Category;
import 'package:niogu_app/core/database/tables/categories.dart';
import 'package:niogu_app/core/database/tables/sync_queues.dart';
import 'package:niogu_app/core/entities/custom_sync_queue.dart';
import 'package:niogu_app/core/enums/sync_status.dart';
import 'package:niogu_app/core/enums/table_type.dart';
import 'package:niogu_app/features/goods/products/domain/entities/category.dart';

part 'category_dao.g.dart';

@DriftAccessor(tables: [Categories, SyncQueues])
class CategoryDao extends DatabaseAccessor<AppDatabase>
    with _$CategoryDaoMixin {
  CategoryDao(AppDatabase db) : super(db);

  Stream<List<Category>> watchAllCategories() async* {
    final query = selectOnly(categories)
      ..addColumns([categories.localId, categories.name])
      ..orderBy([OrderingTerm.desc(categories.createdAt)]);

    yield* query.watch().map((rows) {
      return rows.map((row) {
        final String id = row.read(categories.localId) ?? '';
        final String name = row.read(categories.name) ?? '';
        return Category(id: id, name: name);
      }).toList();
    });
  }

  Future<List<Category>> getAllCategories() async {
    final query = selectOnly(categories)
      ..addColumns([categories.localId, categories.name])
      ..orderBy([OrderingTerm.desc(categories.createdAt)]);

    final results = await query.get();

    return results.map((row) {
      final String id = row.read(categories.localId) ?? '';
      final String name = row.read(categories.name) ?? '';
      return Category(id: id, name: name);
    }).toList();
  }

  Future<Category> getCategoryById(String id) async {
    final query = selectOnly(categories)
      ..addColumns([
        categories.localId,
        categories.name,
        categories.localImagePath,
      ])
      ..where(categories.localId.equals(id))
      ..limit(1);

    final result = await query.getSingle();
    final String localId = result.read(categories.localId) ?? '';
    final String name = result.read(categories.name) ?? '';
    final String imagePath = result.read(categories.localImagePath) ?? '';
    return Category(id: localId, name: name, imagePath: imagePath);
  }

  Future<int> insertCategory(CategoriesCompanion category) async {
    return await transaction(() async {
      final now = DateTime.now();

      final id = await into(categories).insert(category);

      final payload = SyncPayload(
        table: TableType.categories.type,
        localId: category.localId.value,
        action: SyncAction.create.action,
        data: {
          'uuid': category.localId.value,
          'name': category.name.value,
          'server_image_url': category.localImagePath.value,
          'created_at': now.toIso8601String(),
          'updated_at': now.toIso8601String(),
        },
      ).toJson();

      await into(syncQueues).insert(
        SyncQueuesCompanion(
          tableType: Value(TableType.categories.type),
          tableLocalId: category.localId,
          action: Value(SyncAction.create.action),
          status: Value(SyncStatus.dirty.status),
          payload: Value(payload),
        ),
      );

      return id;
    });
  }

  Future<int> updateCategory(String id, String name, String? imagePath) async {
    return await transaction(() async {
      final now = DateTime.now();

      final rowsAffected =
          await (update(
            categories,
          )..where((category) => category.localId.equals(id))).write(
            CategoriesCompanion(
              name: Value(name),
              localImagePath: Value(imagePath),
              syncStatus: Value(SyncStatus.dirty.status),
              updatedAt: Value(now),
            ),
          );

      final payload = SyncPayload(
        table: TableType.categories.type,
        localId: id,
        action: SyncAction.update.action,
        data: {
          'uuid': id,
          'name': name,
          'server_image_url': imagePath,
          'updated_at': now.toIso8601String(),
        },
      ).toJson();

      final syncQueueComp = SyncQueuesCompanion(
        tableType: Value(TableType.categories.type),
        tableLocalId: Value(id),
        action: Value(SyncAction.update.action),
        status: Value(SyncStatus.dirty.status),
        payload: Value(payload),
      );

      final existing =
          await (select(syncQueues)..where(
                (t) =>
                    t.tableType.equals(TableType.categories.type) &
                    t.tableLocalId.equals(id),
              ))
              .getSingleOrNull();

      if (existing != null) {
        await (update(
          syncQueues,
        )..where((q) => q.id.equals(existing.id))).write(syncQueueComp);
      } else {
        await into(syncQueues).insert(syncQueueComp);
      }
      return rowsAffected;
    });
  }
}
