package com.writeoff;

import com.writeoff.module.audit.dto.AuditActionRequest;
import com.writeoff.module.audit.model.AuditTask;
import com.writeoff.module.audit.repository.InMemoryAuditTaskRepository;
import com.writeoff.module.audit.service.AuditService;
import com.writeoff.module.finance.dto.ConfirmPaymentRequest;
import com.writeoff.module.finance.repository.InMemoryPaymentRepository;
import com.writeoff.module.finance.service.FinanceService;
import com.writeoff.module.meeting.dto.CreateMeetingRequest;
import com.writeoff.module.meeting.dto.SubmitMeetingRequest;
import com.writeoff.module.meeting.model.Meeting;
import com.writeoff.module.meeting.repository.InMemoryMeetingRepository;
import com.writeoff.module.meeting.service.MeetingService;
import com.writeoff.module.project.dto.CreateProjectRequest;
import com.writeoff.module.project.model.Project;
import com.writeoff.module.project.repository.InMemoryProjectRepository;
import com.writeoff.module.project.service.ProjectService;
import com.writeoff.module.scheduler.repository.InMemoryAsyncJobRepository;
import com.writeoff.module.scheduler.service.AsyncJobService;
import com.writeoff.security.AuthContext;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.lang.reflect.Field;
import java.time.LocalDate;
import java.util.List;

class MvpFlowIntegrationTest {

    @Test
    void shouldFinishCoreFlow() throws Exception {
        AuthContext.set(1001L, 1L);
        try {
        InMemoryProjectRepository projectRepository = new InMemoryProjectRepository();
        InMemoryMeetingRepository meetingRepository = new InMemoryMeetingRepository();
        InMemoryAuditTaskRepository auditTaskRepository = new InMemoryAuditTaskRepository();
        InMemoryPaymentRepository paymentRepository = new InMemoryPaymentRepository();
        AsyncJobService asyncJobService = new AsyncJobService(new InMemoryAsyncJobRepository());

        ProjectService projectService = new ProjectService(projectRepository);
        MeetingService meetingService = new MeetingService(meetingRepository, projectService, auditTaskRepository, asyncJobService);
        AuditService auditService = new AuditService(auditTaskRepository, meetingService, asyncJobService);
        FinanceService financeService = new FinanceService(paymentRepository, meetingService);

        Project project = projectService.create(new CreateProjectRequestWrapper("P1", 1000000L, 3).toRequest());
        Meeting meeting = meetingService.create(new CreateMeetingRequestWrapper(project.getId(), "M1", 300000L).toRequest());
        meetingService.submit(meeting.getId(), new SubmitMeetingRequestWrapper("submit-key-1", "提交审核").toRequest());

        List<AuditTask> tasks = auditService.listTasks().getList();
        Assertions.assertFalse(tasks.isEmpty());

        AuditTask first = tasks.get(0);
        auditService.approve(first.getId(), new AuditActionRequestWrapper("audit-key-1", "初审通过").toRequest());
        AuditTask second = auditService.listTasks().getList().stream().filter(t -> t.getId() > first.getId()).findFirst().orElseThrow(IllegalStateException::new);
        auditService.approve(second.getId(), new AuditActionRequestWrapper("audit-key-2", "复审通过").toRequest());
        AuditTask third = auditService.listTasks().getList().stream().filter(t -> t.getId() > second.getId()).findFirst().orElseThrow(IllegalStateException::new);
        auditService.approve(third.getId(), new AuditActionRequestWrapper("audit-key-3", "终审通过").toRequest());

        Object paymentResult = financeService.confirmPayment(new ConfirmPaymentRequestWrapper(
                "pay-key-1", project.getId(), meeting.getId(), 200000L, "oss/voucher.pdf").toRequest());
        Assertions.assertNotNull(paymentResult);
        } finally {
            AuthContext.clear();
        }
    }

    private static class CreateProjectRequestWrapper {
        private final String name;
        private final long budgetCent;
        private final int meetingTotal;

        private CreateProjectRequestWrapper(String name, long budgetCent, int meetingTotal) {
            this.name = name;
            this.budgetCent = budgetCent;
            this.meetingTotal = meetingTotal;
        }

        private CreateProjectRequest toRequest() throws Exception {
            CreateProjectRequest request = new CreateProjectRequest();
            setField(request, "name", name);
            setField(request, "startDate", LocalDate.of(2026, 1, 1));
            setField(request, "endDate", LocalDate.of(2026, 12, 31));
            setField(request, "budgetCent", budgetCent);
            setField(request, "meetingTotal", meetingTotal);
            return request;
        }
    }

    private static class CreateMeetingRequestWrapper {
        private final long projectId;
        private final String topic;
        private final long budgetCent;

        private CreateMeetingRequestWrapper(long projectId, String topic, long budgetCent) {
            this.projectId = projectId;
            this.topic = topic;
            this.budgetCent = budgetCent;
        }

        private CreateMeetingRequest toRequest() throws Exception {
            CreateMeetingRequest request = new CreateMeetingRequest();
            setField(request, "projectId", projectId);
            setField(request, "topic", topic);
            setField(request, "budgetCent", budgetCent);
            setField(request, "meetingCategory", "学术会");
            setField(request, "meetingForm", "线下");
            setField(request, "location", "线下");
            setField(request, "startTime", "2026-01-10 09:00:00");
            setField(request, "endTime", "2026-01-10 18:00:00");
            return request;
        }
    }

    private static class SubmitMeetingRequestWrapper {
        private final String key;
        private final String remark;

        private SubmitMeetingRequestWrapper(String key, String remark) {
            this.key = key;
            this.remark = remark;
        }

        private SubmitMeetingRequest toRequest() throws Exception {
            SubmitMeetingRequest request = new SubmitMeetingRequest();
            setField(request, "idempotencyKey", key);
            setField(request, "remark", remark);
            return request;
        }
    }

    private static class AuditActionRequestWrapper {
        private final String key;
        private final String opinion;

        private AuditActionRequestWrapper(String key, String opinion) {
            this.key = key;
            this.opinion = opinion;
        }

        private AuditActionRequest toRequest() throws Exception {
            AuditActionRequest request = new AuditActionRequest();
            setField(request, "idempotencyKey", key);
            setField(request, "opinion", opinion);
            return request;
        }
    }

    private static class ConfirmPaymentRequestWrapper {
        private final String key;
        private final long projectId;
        private final long meetingId;
        private final long amount;
        private final String voucher;

        private ConfirmPaymentRequestWrapper(String key, long projectId, long meetingId, long amount, String voucher) {
            this.key = key;
            this.projectId = projectId;
            this.meetingId = meetingId;
            this.amount = amount;
            this.voucher = voucher;
        }

        private ConfirmPaymentRequest toRequest() throws Exception {
            ConfirmPaymentRequest request = new ConfirmPaymentRequest();
            setField(request, "idempotencyKey", key);
            setField(request, "projectId", projectId);
            setField(request, "meetingId", meetingId);
            setField(request, "amountCent", amount);
            setField(request, "paymentVoucherOssKey", voucher);
            return request;
        }
    }

    private static void setField(Object target, String fieldName, Object value) throws Exception {
        Field field = target.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(target, value);
    }
}
