import 'dart:convert';

import 'package:fis_jsonrpc/rpc.dart';
import 'package:vital_local_database/index.dart';
import 'package:vitalapp/database/db.dart';
import 'package:vitalapp/database/entities/patient.dart';
import 'package:fis_common/logger/logger.dart';

class PatientServiceMock extends VitalPatientService {
  PatientServiceMock(String host) : super(host);

  @override
  Future<String> createPatientAsync(CreatePatientRequest request) async {
    final code = request.code!;
    final existEntity = await _checkPatientExist(code);

    PatientEntity entity;
    PatientDTO dto;
    if (existEntity == null) {
      entity = PatientEntity();
      entity.code = request.code!;
      entity.isValid = true;
      entity.name = request.patientName!;

      dto = PatientDTO();
      _ModelConverter.syncCreate2Dto(dto, request);

      final dtoJson = dto.toJson();
      entity.dataJson = jsonEncode(dtoJson);

      final id = await db.repositories.patient.insert(entity);
      return id > 0 ? code : "";
    } else {
      entity = existEntity;
      final jsonMap = jsonDecode(entity.dataJson);
      dto = PatientDTO.fromJson(jsonMap);
      _ModelConverter.syncCreate2Dto(dto, request);

      final dtoJson = dto.toJson();
      entity.dataJson = jsonEncode(dtoJson);

      final rows = await db.repositories.patient.update(entity);
      return rows > 0 ? code : "";
    }
  }

  @override
  Future<bool> updatePatientAsync(UpdatePatientRequest request) async {
    final code = request.code!;
    PatientEntity? entity = await _checkPatientExist(code);
    PatientDTO dto;
    if (entity == null) {
      entity = PatientEntity();
      entity.isValid = true;

      dto = PatientDTO();
    } else {
      final jsonMap = jsonDecode(entity.dataJson);
      dto = PatientDTO.fromJson(jsonMap);
    }
    _ModelConverter.syncUpdate2Dto(dto, request);

    entity.dataJson = jsonEncode(dto);

    final rows = await db.repositories.patient.update(entity);
    return rows > 0;
  }

  @override
  Future<PatientDTO> getPatientDetailAsync(GetPatientRequest request) async {
    final code = request.code!;
    final entity = await db.repositories.patient.singleByCode(code);
    if (entity == null || entity.dataJson.isEmpty) {
      throw JsonRpcException(code: -1, message: "Offline patient not found");
    }
    final jsonMap = jsonDecode(entity.dataJson);
    final dto = PatientDTO.fromJson(jsonMap);
    return dto;
  }

  @override
  Future<PageCollection<PatientDTO>> getPatientPageAsync(
      PatientPageRequest request) async {
    final query = db.repositories.patient.queryable.where((x) {
      final List<IDbColumnCondition> arr = [];
      arr.add(x.isValid.equals(true));
      final keyword = request.keyword;
      if (keyword != null && keyword.isNotEmpty) {
        // TODO: 暂时只支持姓名模糊
        arr.add(x.name.like("%$keyword%"));
      } else {
        if (request.startTime != null) {
          arr.add(x.createTime.gte(request.startTime));
        }
        if (request.endTime != null) {
          arr.add(x.createTime.lt(request.endTime));
        }
      }
      return arr;
    });

    final count = await query.count();
    final offset = request.pageSize * (request.pageIndex - 1);
    final entities =
        await query.offset(offset).limit(request.pageSize).toList();

    final dtos = entities.map((e) {
      final jsonMap = jsonDecode(e.dataJson);
      final dto = PatientDTO.fromJson(jsonMap);
      return dto;
    }).toList();

    final pagedList = PageCollection<PatientDTO>(
      pageIndex: request.pageIndex,
      pageSize: request.pageSize,
      dataCount: count,
      pageData: dtos,
    );
    return pagedList;
  }

  Future<PatientEntity?> _checkPatientExist(String code) async {
    try {
      final entity = await db.repositories.patient.singleByCode(code);
      return entity;
    } catch (e) {
      logger.e(
          "PatientServiceMock - Check patient exist error. Code: $code.", e);
    }
    return null;
  }
}

abstract class _ModelConverter {
  /// 根据新建请求创建DTO
  static void syncCreate2Dto(PatientDTO dto, CreatePatientRequest request) {
    dto.code = request.code;
    dto.patientName = request.patientName;
    dto.phone = request.phone;
    dto.cardNo = request.cardNo;
    dto.nationality = request.nationality;
    dto.birthday = request.birthday;
    dto.crowdLabels = request.crowdLabels;
    dto.cardType = request.cardType;
    dto.patientGender = request.patientGender;
    dto.patientAddress = request.patientAddress;
    dto.permanentResidenceAddress = request.permanentResidenceAddress;
    dto.teamRegionCode = request.teamRegionCode;
    dto.contractedDoctor = request.contractedDoctor;
  }

  /// 根据DTO创建新建请求
  static CreatePatientRequest dto2Create(PatientDTO dto) {
    final dtoJson = dto.toJson();
    final request = CreatePatientRequest.fromJson(dtoJson);
    return request;
  }

  /// 同步更新到DTO
  static void syncUpdate2Dto(PatientDTO dto, UpdatePatientRequest request) {
    dto.code = request.code;
    dto.patientName = request.patientName;
    dto.phone = request.phone;
    dto.cardNo = request.cardNo;
    dto.nationality = request.nationality;
    dto.birthday = request.birthday;
    dto.crowdLabels = request.crowdLabels;
    if (request.cardType != null) {
      dto.cardType = request.cardType!;
    }
    if (request.patientGender != null) {
      dto.patientGender = request.patientGender!;
    }
    dto.patientAddress = request.patientAddress;
    dto.permanentResidenceAddress = request.permanentResidenceAddress;
    dto.teamRegionCode = request.teamRegionCode;
    dto.contractedDoctor = request.contractedDoctor;
  }

  /// 根据DTO创建更新请求
  static UpdatePatientRequest dto2Update(PatientDTO dto) {
    final dtoJson = dto.toJson();
    final request = UpdatePatientRequest.fromJson(dtoJson);
    return request;
  }
}
