package ru.soune.nocopy.controller;


import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import ru.soune.nocopy.dto.payout.*;
import ru.soune.nocopy.entity.payout.BankTransferPayoutMethod;
import ru.soune.nocopy.entity.payout.CardPayoutMethod;
import ru.soune.nocopy.entity.payout.PayoutRequest;
import ru.soune.nocopy.entity.payout.PayoutType;
import ru.soune.nocopy.entity.user.AuthToken;
import ru.soune.nocopy.entity.user.User;
import ru.soune.nocopy.exception.InsufficientFundsException;
import ru.soune.nocopy.exception.InvalidPayoutMethodException;
import ru.soune.nocopy.exception.NotFoundAuthToken;
import ru.soune.nocopy.exception.PendingPayoutExistsException;
import ru.soune.nocopy.repository.AuthTokenRepository;
import ru.soune.nocopy.service.payout.PayoutMethodService;
import ru.soune.nocopy.service.payout.PayoutRequestService;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/api/payouts")
@RequiredArgsConstructor
@Slf4j
public class PayoutController {

    private final PayoutRequestService payoutRequestService;

    private final PayoutMethodService payoutMethodService;

    private final AuthTokenRepository authTokenRepository;

    /*
       Create payout
     */
    @PostMapping("/create-request")
    public ResponseEntity<?> createPayoutRequest(@RequestHeader("Authorization") String tokenHeader,
                                                 @Valid @RequestBody PayoutRequestDTO request) {
        String token = tokenHeader.replace("Bearer ", "");

        Optional<AuthToken> tokenOptional = authTokenRepository.findByToken(token);

        AuthToken authToken = tokenOptional.orElseThrow(() -> new NotFoundAuthToken("Token: " + token + "not found"));

        User user = authToken.getUser();

        try {
            PayoutRequest payoutRequest = payoutRequestService.createPayoutRequest(user.getId(), request);
            return ResponseEntity.status(HttpStatus.CREATED).body(payoutRequest);
        } catch (InsufficientFundsException | PendingPayoutExistsException | InvalidPayoutMethodException e) {
            return ResponseEntity.status(HttpStatus.CONFLICT).body(e.getMessage());
        }
    }

    /*
        Completed payout
     */
    @PostMapping("/requests/{requestId}/process")
    public ResponseEntity<PayoutRequest> processPayoutRequest(@PathVariable Long requestId) {
        PayoutRequest processed = payoutRequestService.processRequest(requestId);

        return ResponseEntity.ok(processed);
    }

    /*
       Cancel payout
     */
    @PostMapping("/requests/{requestId}/cancel")
    public ResponseEntity<PayoutRequest> cancelPayoutRequest(@PathVariable Long requestId,
                                                             @RequestHeader("Authorization") String tokenHeader) {
        String token = tokenHeader.replace("Bearer ", "");

        Optional<AuthToken> tokenOptional = authTokenRepository.findByToken(token);

        AuthToken authToken = tokenOptional.orElseThrow(() -> new NotFoundAuthToken("Token: " + token + "not found"));

        User user = authToken.getUser();

        PayoutRequest cancelled = payoutRequestService.cancelPayoutRequest(user.getId(), requestId);

        return ResponseEntity.ok(cancelled);
    }

    /*
       Get user payouts
     */
    @GetMapping("/requests")
    public ResponseEntity<List<PayoutRequest>> getUserRequests(@RequestHeader("Authorization") String tokenHeader) {
        String token = tokenHeader.replace("Bearer ", "");

        Optional<AuthToken> tokenOptional = authTokenRepository.findByToken(token);

        AuthToken authToken = tokenOptional.orElseThrow(() -> new NotFoundAuthToken("Token: " + token + "not found"));

        User user = authToken.getUser();

        return ResponseEntity.ok(payoutRequestService.getUserPayoutRequests(user.getId()));
    }

    /*
         Get payout by id
    */
    @GetMapping("/requests/{requestId}")
    public ResponseEntity<PayoutRequest> getPayoutRequest(@PathVariable Long requestId,
                                                          @RequestHeader("Authorization") String tokenHeader) {
        String token = tokenHeader.replace("Bearer ", "");

        Optional<AuthToken> tokenOptional = authTokenRepository.findByToken(token);

        AuthToken authToken = tokenOptional.orElseThrow(() -> new NotFoundAuthToken("Token: " + token + "not found"));

        User user = authToken.getUser();

        return ResponseEntity.ok(payoutRequestService.getPayoutRequest(user.getId(), requestId));
    }

    /*
             Get payout by id
     */
    @GetMapping("user/payout-methods")
    public ResponseEntity<List<PayoutMethodDTO>> getUserMethods(@RequestHeader("Authorization") String tokenHeader) {
        String token = tokenHeader.replace("Bearer ", "");

        Optional<AuthToken> tokenOptional = authTokenRepository.findByToken(token);

        AuthToken authToken = tokenOptional.orElseThrow(() -> new NotFoundAuthToken("Token: " + token + "not found"));

        User user = authToken.getUser();

        return ResponseEntity.ok(payoutMethodService.getUserMethods(user.getId()));
    }

    /*
         Get payout-methods
     */
    @GetMapping("/payout-methods")
    public ResponseEntity<List<PayoutMethodTypeDTO>> getAvailableMethodTypes() {
        List<PayoutMethodTypeDTO> types = Arrays.stream(PayoutType.values())
                .map(type -> PayoutMethodTypeDTO.builder()
                        .code(type.name())
                        .name(type.getDisplayName())
                        .build())
                .collect(Collectors.toList());

        return ResponseEntity.ok(types);
    }
    /*
         add card method by userId
       */
    @PostMapping("/payout-method/card")
    public ResponseEntity<PayoutMethodDTO> addCardMethod(@Valid @RequestBody AddCardRequest request,
                                                         @RequestHeader("Authorization") String tokenHeader) {
        String token = tokenHeader.replace("Bearer ", "");

        Optional<AuthToken> tokenOptional = authTokenRepository.findByToken(token);

        AuthToken authToken = tokenOptional.orElseThrow(() -> new NotFoundAuthToken("Token: " + token + "not found"));

        User user = authToken.getUser();

        CardPayoutMethod method = payoutMethodService.addCardMethod(user.getId(), request);

        return ResponseEntity.status(HttpStatus.CREATED)
                .body(payoutMethodService.convertToDto(method));
    }

    /*
         add bank payout-method
     */
    @PostMapping("/payout-method/bank-transfer")
    public ResponseEntity<PayoutMethodDTO> addBankTransferMethod(@Valid @RequestBody AddBankTransferRequest request,
                                                                 @RequestHeader("Authorization") String tokenHeader) {
        String token = tokenHeader.replace("Bearer ", "");

        Optional<AuthToken> tokenOptional = authTokenRepository.findByToken(token);

        AuthToken authToken = tokenOptional.orElseThrow(() -> new NotFoundAuthToken("Token: " + token + "not found"));

        User user = authToken.getUser();

        BankTransferPayoutMethod method = payoutMethodService.addBankTransferMethod(user.getId(), request);

        return ResponseEntity.status(HttpStatus.CREATED)
                .body(payoutMethodService.convertToDto(method));
    }


    /*
         set default bank payout-method
     */
    @PutMapping("/payout-method/{methodId}/default")
    public ResponseEntity<Void> setDefaultMethod(@PathVariable Long methodId,
                                                 @RequestHeader("Authorization") String tokenHeader) {
        String token = tokenHeader.replace("Bearer ", "");

        Optional<AuthToken> tokenOptional = authTokenRepository.findByToken(token);

        AuthToken authToken = tokenOptional.orElseThrow(() -> new NotFoundAuthToken("Token: " + token + "not found"));

        User user = authToken.getUser();

        payoutMethodService.setDefaultMethod(user.getId(), methodId);

        return ResponseEntity.ok().build();
    }
}
