import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:get_it/get_it.dart';
import 'package:shadcn_ui/shadcn_ui.dart';

import 'package:superport_v2/core/common/models/paginated_result.dart';
import 'package:superport_v2/core/config/environment.dart';
import 'package:superport_v2/core/permissions/permission_manager.dart';
import 'package:superport_v2/core/permissions/permission_resources.dart';
import 'package:superport_v2/features/approvals/domain/entities/approval.dart';
import 'package:superport_v2/features/approvals/domain/entities/approval_template.dart';
import 'package:superport_v2/features/approvals/domain/entities/approval_proceed_status.dart';
import 'package:superport_v2/features/approvals/domain/repositories/approval_repository.dart';
import 'package:superport_v2/features/approvals/domain/repositories/approval_template_repository.dart';
import 'package:superport_v2/features/approvals/presentation/pages/approval_page.dart';
import 'package:superport_v2/features/inventory/lookups/domain/entities/lookup_item.dart';
import 'package:superport_v2/features/inventory/lookups/domain/repositories/inventory_lookup_repository.dart';
import 'package:superport_v2/widgets/components/superport_detail_dialog.dart';

import '../../helpers/test_app.dart';
import '../../helpers/fixture_loader.dart';

void main() {
  TestWidgetsFlutterBinding.ensureInitialized();

  late Map<String, dynamic> permissionFixture;
  late Set<PermissionAction> viewerPermissions;
  late Set<PermissionAction> approverPermissions;

  Set<PermissionAction> parseActions(String key) {
    final raw = permissionFixture[key];
    if (raw is! List) {
      return {};
    }
    return raw
        .whereType<String>()
        .map(
          (action) => PermissionAction.values.firstWhere(
            (candidate) => candidate.name == action,
            orElse: () => PermissionAction.view,
          ),
        )
        .toSet();
  }

  Future<void> selectDetailTab(WidgetTester tester, String id) async {
    final tabsFinder = find.descendant(
      of: find.byType(SuperportDetailDialog),
      matching: find.byType(ShadTabs<String>),
    );
    final tabsState = tester.state(tabsFinder);
    final controller =
        (tabsState as dynamic).controller as ShadTabsController<String>;
    controller.select(id);
    await tester.pump();
    await tester.pumpAndSettle();
  }

  setUpAll(() async {
    dotenv.testLoad(fileInput: 'FEATURE_APPROVALS_ENABLED=true');
    await Environment.initialize();
    dotenv.env['FEATURE_APPROVALS_ENABLED'] = 'true';
    permissionFixture = loadJsonFixture('approvals/approval_permissions.json');
    viewerPermissions = parseActions('viewer');
    approverPermissions = parseActions('approver');
  });

  tearDown(() async {
    await GetIt.I.reset();
  });

  Future<void> pumpApprovalPage(
    WidgetTester tester,
    PermissionManager manager,
  ) async {
    await tester.pumpWidget(
      buildTestApp(const ApprovalPage(), permissionManager: manager),
    );
    await tester.pump(const Duration(milliseconds: 200));
    await tester.pumpAndSettle();
  }

  testWidgets('결재 단계 액션은 승인 권한이 없으면 비활성화된다', (tester) async {
    final repo = _StubApprovalRepository();
    final templateRepo = _StubApprovalTemplateRepository();
    final lookupRepo = _StubInventoryLookupRepository();
    GetIt.I.registerSingleton<ApprovalRepository>(repo);
    GetIt.I.registerSingleton<ApprovalTemplateRepository>(templateRepo);
    GetIt.I.registerSingleton<InventoryLookupRepository>(lookupRepo);

    final permissionManager = PermissionManager(
      overrides: {PermissionResources.approvals: viewerPermissions},
    );

    final view = tester.view;
    view.physicalSize = const Size(1280, 800);
    view.devicePixelRatio = 1.0;
    addTearDown(() {
      view.resetPhysicalSize();
      view.resetDevicePixelRatio();
    });

    await pumpApprovalPage(tester, permissionManager);

    final rowFinder = find.byKey(const ValueKey('approval_row_1'));
    expect(rowFinder, findsOneWidget);

    await tester.tap(rowFinder);
    await tester.pumpAndSettle();

    await selectDetailTab(tester, 'steps');

    final approveButton = tester.widget<ShadButton>(
      find.byKey(const ValueKey('step_action_100_approve')),
    );

    expect(approveButton.onPressed, isNull);
    expect(find.text('결재 권한이 없어 단계 행위를 실행할 수 없습니다.'), findsOneWidget);
  });

  testWidgets('승인 권한이 있으면 단계 액션을 실행할 수 있다', (tester) async {
    final repo = _StubApprovalRepository();
    final templateRepo = _StubApprovalTemplateRepository();
    final lookupRepo = _StubInventoryLookupRepository();
    GetIt.I.registerSingleton<ApprovalRepository>(repo);
    GetIt.I.registerSingleton<ApprovalTemplateRepository>(templateRepo);
    GetIt.I.registerSingleton<InventoryLookupRepository>(lookupRepo);

    final permissionManager = PermissionManager(
      overrides: {PermissionResources.approvals: approverPermissions},
    );

    await pumpApprovalPage(tester, permissionManager);

    final rowFinder = find.byKey(const ValueKey('approval_row_1'));
    expect(rowFinder, findsOneWidget);

    await tester.tap(rowFinder);
    await tester.pumpAndSettle();

    await selectDetailTab(tester, 'steps');

    final approveButton = tester.widget<ShadButton>(
      find.byKey(const ValueKey('step_action_100_approve')),
    );

    expect(approveButton.onPressed, isNotNull);
    expect(find.text('결재 권한이 없어 단계 행위를 실행할 수 없습니다.'), findsNothing);
  });

  testWidgets('canProceed가 false면 단계 버튼을 비활성화하고 이유를 안내한다', (tester) async {
    final repo = _BlockingApprovalRepository();
    final templateRepo = _StubApprovalTemplateRepository();
    final lookupRepo = _StubInventoryLookupRepository();
    GetIt.I.registerSingleton<ApprovalRepository>(repo);
    GetIt.I.registerSingleton<ApprovalTemplateRepository>(templateRepo);
    GetIt.I.registerSingleton<InventoryLookupRepository>(lookupRepo);

    final permissionManager = PermissionManager(
      overrides: {PermissionResources.approvals: approverPermissions},
    );

    await pumpApprovalPage(tester, permissionManager);

    final rowFinder = find.byKey(const ValueKey('approval_row_1'));
    expect(rowFinder, findsOneWidget);

    await tester.tap(rowFinder);
    await tester.pumpAndSettle();

    await selectDetailTab(tester, 'steps');

    expect(find.textContaining('선행 단계가 완료되지 않았습니다.'), findsWidgets);

    final approveButton = tester.widget<ShadButton>(
      find.byKey(const ValueKey('step_action_100_approve')),
    );
    expect(approveButton.onPressed, isNull);
  });
}

class _StubApprovalRepository implements ApprovalRepository {
  _StubApprovalRepository();

  final ApprovalStatus _pendingStatus = ApprovalStatus(id: 1, name: '승인대기');
  final ApprovalApprover _approver = ApprovalApprover(
    id: 10,
    employeeNo: 'E010',
    name: '김승인',
  );

  late final ApprovalStep _step = ApprovalStep(
    id: 100,
    stepOrder: 1,
    approver: _approver,
    status: _pendingStatus,
    assignedAt: DateTime(2024, 1, 1),
  );

  late final Approval _approval = Approval(
    id: 1,
    approvalNo: 'AP-001',
    transactionNo: 'TRX-001',
    status: _pendingStatus,
    currentStep: _step,
    requester: ApprovalRequester(id: 20, employeeNo: 'E020', name: '요청자'),
    requestedAt: DateTime(2024, 1, 1),
    steps: [_step],
    histories: const [],
  );

  @override
  Future<PaginatedResult<Approval>> list({
    int page = 1,
    int pageSize = 20,
    int? transactionId,
    int? approvalStatusId,
    int? requestedById,
    List<String>? statusCodes,
    bool includePending = false,
    bool includeHistories = false,
    bool includeSteps = false,
  }) async {
    return PaginatedResult<Approval>(
      items: [_approval],
      page: 1,
      pageSize: 20,
      total: 1,
    );
  }

  @override
  Future<Approval> fetchDetail(
    int id, {
    bool includeSteps = true,
    bool includeHistories = true,
  }) async {
    return _approval;
  }

  @override
  Future<Approval> submit(ApprovalSubmissionInput input) async {
    return _approval;
  }

  @override
  Future<Approval> resubmit(ApprovalResubmissionInput input) async {
    return _approval;
  }

  @override
  Future<Approval> approve(ApprovalDecisionInput input) async {
    return _approval;
  }

  @override
  Future<Approval> reject(ApprovalDecisionInput input) async {
    return _approval;
  }

  @override
  Future<Approval> recall(ApprovalRecallInput input) async {
    return _approval;
  }

  @override
  Future<List<ApprovalAction>> listActions({bool activeOnly = true}) async {
    return [
      ApprovalAction(id: 1, name: 'approve'),
      ApprovalAction(id: 2, name: 'reject'),
      ApprovalAction(id: 3, name: 'comment'),
    ];
  }

  @override
  Future<PaginatedResult<ApprovalHistory>> listHistory({
    required int approvalId,
    int page = 1,
    int pageSize = 20,
    DateTime? from,
    DateTime? to,
    int? actorId,
    int? approvalActionId,
  }) async {
    return PaginatedResult(
      items: const <ApprovalHistory>[],
      page: page,
      pageSize: pageSize,
      total: 0,
    );
  }

  @override
  Future<Approval> performStepAction(ApprovalStepActionInput input) async {
    return _approval;
  }

  @override
  Future<Approval> assignSteps(ApprovalStepAssignmentInput input) async {
    return _approval;
  }

  @override
  Future<ApprovalProceedStatus> canProceed(int id) async {
    return ApprovalProceedStatus(approvalId: id, canProceed: true);
  }

  @override
  Future<Approval> create(ApprovalCreateInput input) {
    throw UnimplementedError();
  }

  @override
  Future<void> delete(int id) {
    throw UnimplementedError();
  }

  @override
  Future<Approval> restore(int id) {
    throw UnimplementedError();
  }

  @override
  Future<Approval> update(ApprovalUpdateInput input) {
    throw UnimplementedError();
  }
}

class _BlockingApprovalRepository extends _StubApprovalRepository {
  @override
  Future<ApprovalProceedStatus> canProceed(int id) async {
    return ApprovalProceedStatus(
      approvalId: id,
      canProceed: false,
      reason: '선행 단계가 완료되지 않았습니다. 관리자에게 문의하세요.',
    );
  }
}

class _StubApprovalTemplateRepository implements ApprovalTemplateRepository {
  _StubApprovalTemplateRepository();

  final ApprovalTemplate _template = ApprovalTemplate(
    id: 1,
    code: 'TMP-001',
    name: '표준 1단계',
    isActive: true,
    steps: [
      ApprovalTemplateStep(
        stepOrder: 1,
        approver: ApprovalTemplateApprover(
          id: 10,
          employeeNo: 'E010',
          name: '김승인',
        ),
      ),
    ],
  );

  @override
  Future<PaginatedResult<ApprovalTemplate>> list({
    int page = 1,
    int pageSize = 20,
    String? query,
    bool? isActive,
  }) async {
    return PaginatedResult<ApprovalTemplate>(
      items: [_template],
      page: 1,
      pageSize: 20,
      total: 1,
    );
  }

  @override
  Future<ApprovalTemplate> fetchDetail(
    int id, {
    bool includeSteps = true,
  }) async {
    return _template;
  }

  @override
  Future<ApprovalTemplate> create(
    ApprovalTemplateInput input, {
    List<ApprovalTemplateStepInput> steps = const [],
  }) {
    throw UnimplementedError();
  }

  @override
  Future<void> delete(int id) {
    throw UnimplementedError();
  }

  @override
  Future<ApprovalTemplate> restore(int id) {
    throw UnimplementedError();
  }

  @override
  Future<ApprovalTemplate> update(
    int id,
    ApprovalTemplateInput input, {
    List<ApprovalTemplateStepInput>? steps,
  }) {
    throw UnimplementedError();
  }
}

class _StubInventoryLookupRepository implements InventoryLookupRepository {
  @override
  Future<List<LookupItem>> fetchTransactionTypes({
    bool activeOnly = true,
  }) async {
    return <LookupItem>[
      LookupItem(id: 1, name: '입고', code: 'INBOUND'),
      LookupItem(id: 2, name: '출고', code: 'OUTBOUND'),
    ];
  }

  @override
  Future<List<LookupItem>> fetchTransactionStatuses({
    bool activeOnly = true,
  }) async {
    return <LookupItem>[
      LookupItem(id: 10, name: '승인대기', code: 'pending'),
      LookupItem(id: 11, name: '진행중', code: 'in_progress'),
    ];
  }

  @override
  Future<List<LookupItem>> fetchApprovalStatuses({
    bool activeOnly = true,
  }) async {
    return <LookupItem>[
      LookupItem(id: 20, name: '승인대기', code: 'pending'),
      LookupItem(id: 21, name: '진행중', code: 'in_progress'),
    ];
  }

  @override
  Future<List<LookupItem>> fetchApprovalActions({
    bool activeOnly = true,
  }) async {
    return <LookupItem>[
      LookupItem(id: 30, name: '승인', code: 'approve'),
      LookupItem(id: 31, name: '반려', code: 'reject'),
    ];
  }
}
