import 'package:dio/dio.dart';

import '../../../../core/common/models/paginated_result.dart';
import '../../../../core/network/api_client.dart';
import '../../../../core/network/api_routes.dart';
import '../../domain/entities/approval_template.dart';
import '../../domain/repositories/approval_template_repository.dart';
import '../dtos/approval_template_dto.dart';

/// 결재 템플릿 관련 API를 호출하는 원격 저장소.
///
/// - 템플릿/단계 CRUD와 복구 API를 캡슐화한다.
/// - 단계 등록은 별도 엔드포인트로 처리한다.
class ApprovalTemplateRepositoryRemote implements ApprovalTemplateRepository {
  ApprovalTemplateRepositoryRemote({required ApiClient apiClient})
    : _api = apiClient;

  final ApiClient _api;

  static const _basePaths = <String>[
    ApiRoutes.approvalTemplates,
    ApiRoutes.approvalTemplatesLegacy,
  ];

  /// 결재 템플릿 목록을 조회한다. 검색/활성 여부 필터를 지원한다.
  @override
  Future<PaginatedResult<ApprovalTemplate>> list({
    int page = 1,
    int pageSize = 20,
    String? query,
    bool? isActive,
  }) async {
    return _withTemplateRoute((basePath) async {
      final response = await _api.get<Map<String, dynamic>>(
        basePath,
        query: {
          'page': page,
          'page_size': pageSize,
          if (query != null && query.isNotEmpty) 'q': query,
          if (isActive != null) 'active': isActive,
        },
        options: Options(responseType: ResponseType.json),
      );
      return ApprovalTemplateDto.parsePaginated(response.data);
    });
  }

  /// 템플릿 상세 정보를 조회한다. 필요 시 단계 포함 여부를 지정한다.
  @override
  Future<ApprovalTemplate> fetchDetail(
    int id, {
    bool includeSteps = true,
  }) async {
    return _withTemplateRoute((basePath) async {
      final path = ApiClient.buildPath(basePath, [id]);
      final response = await _api.get<Map<String, dynamic>>(
        path,
        query: {if (includeSteps) 'include': 'steps'},
        options: Options(responseType: ResponseType.json),
      );
      return ApprovalTemplateDto.fromJson(
        _api.unwrapAsMap(response),
      ).toEntity(includeSteps: includeSteps);
    });
  }

  /// 템플릿을 생성하고 필요하면 단계까지 함께 등록한다.
  @override
  Future<ApprovalTemplate> create(
    ApprovalTemplateInput input, {
    List<ApprovalTemplateStepInput> steps = const [],
  }) async {
    return _withTemplateRoute((basePath) async {
      final response = await _api.post<Map<String, dynamic>>(
        basePath,
        data: input.toCreatePayload(),
        options: Options(responseType: ResponseType.json),
      );
      final created = ApprovalTemplateDto.fromJson(
        _api.unwrapAsMap(response),
      ).toEntity(includeSteps: false);
      if (steps.isNotEmpty) {
        await _postSteps(created.id, steps, basePath: basePath);
      }
      return fetchDetail(created.id, includeSteps: true);
    });
  }

  /// 템플릿 기본 정보와 단계 구성을 수정한다.
  @override
  Future<ApprovalTemplate> update(
    int id,
    ApprovalTemplateInput input, {
    List<ApprovalTemplateStepInput>? steps,
  }) async {
    return _withTemplateRoute((basePath) async {
      final path = ApiClient.buildPath(basePath, [id]);
      await _api.patch<Map<String, dynamic>>(
        path,
        data: input.toUpdatePayload(id),
        options: Options(responseType: ResponseType.json),
      );
      if (steps != null) {
        await _patchSteps(id, steps, basePath: basePath);
      }
      return fetchDetail(id, includeSteps: true);
    });
  }

  /// 템플릿을 삭제한다.
  @override
  Future<void> delete(int id) async {
    await _withTemplateRoute((basePath) async {
      final path = ApiClient.buildPath(basePath, [id]);
      await _api.delete<void>(path);
    });
  }

  /// 삭제된 템플릿을 복구한다.
  @override
  Future<ApprovalTemplate> restore(int id) async {
    return _withTemplateRoute((basePath) async {
      final path = ApiClient.buildPath(basePath, [id, 'restore']);
      final response = await _api.post<Map<String, dynamic>>(
        path,
        options: Options(responseType: ResponseType.json),
      );
      return ApprovalTemplateDto.fromJson(
        _api.unwrapAsMap(response),
      ).toEntity(includeSteps: false);
    });
  }

  /// 템플릿 단계 전체를 신규로 등록한다.
  Future<void> _postSteps(
    int templateId,
    List<ApprovalTemplateStepInput> steps, {
    required String basePath,
  }) async {
    if (steps.isEmpty) return;
    final path = ApiClient.buildPath(basePath, [templateId, 'steps']);
    await _api.post<Map<String, dynamic>>(
      path,
      data: {
        'id': templateId,
        'steps': steps.map((step) => step.toJson(includeId: false)).toList(),
      },
      options: Options(responseType: ResponseType.json),
    );
  }

  /// 템플릿 단계 정보를 부분 수정한다.
  Future<void> _patchSteps(
    int templateId,
    List<ApprovalTemplateStepInput> steps, {
    required String basePath,
  }) async {
    final path = ApiClient.buildPath(basePath, [templateId, 'steps']);
    await _api.patch<Map<String, dynamic>>(
      path,
      data: {
        'id': templateId,
        'steps': steps.map((step) => step.toJson()).toList(),
      },
      options: Options(responseType: ResponseType.json),
    );
  }

  Future<T> _withTemplateRoute<T>(
    Future<T> Function(String basePath) operation,
  ) async {
    DioException? lastNotFound;
    for (final basePath in _basePaths) {
      try {
        return await operation(basePath);
      } on DioException catch (error) {
        if (error.response?.statusCode == 404) {
          lastNotFound = error;
          continue;
        }
        rethrow;
      }
    }
    if (lastNotFound != null) {
      throw lastNotFound;
    }
    throw StateError('템플릿 경로 후보가 정의되지 않았습니다.');
  }
}
