package com.emonster.taroaichat.web.rest;

import com.emonster.taroaichat.repository.DonationRepository;
import com.emonster.taroaichat.service.DonationQueryService;
import com.emonster.taroaichat.service.DonationService;
import com.emonster.taroaichat.service.UserProfileService;
import com.emonster.taroaichat.service.criteria.DonationCriteria;
import com.emonster.taroaichat.service.dto.DonationDTO;
import com.emonster.taroaichat.service.dto.DonationCreatePaymentIntentDTO;
import com.emonster.taroaichat.service.dto.DonationConfirmPaymentDTO;
import com.emonster.taroaichat.web.rest.errors.BadRequestAlertException;
import com.emonster.taroaichat.web.rest.vm.DonationPaymentIntentVM;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import java.net.URI;
import java.net.URISyntaxException;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import tech.jhipster.web.util.HeaderUtil;
import tech.jhipster.web.util.PaginationUtil;
import tech.jhipster.web.util.ResponseUtil;

/**
 * REST controller for managing {@link com.emonster.taroaichat.domain.Donation}.
 */
@RestController
@RequestMapping("/api/v1/donations")
public class DonationResource {

    private static final Logger LOG = LoggerFactory.getLogger(DonationResource.class);

    private static final String ENTITY_NAME = "donation";

    @Value("${jhipster.clientApp.name}")
    private String applicationName;

    private final DonationService donationService;

    private final DonationRepository donationRepository;

    private final DonationQueryService donationQueryService;

    private final UserProfileService userProfileService;

    public DonationResource(
        DonationService donationService,
        DonationRepository donationRepository,
        DonationQueryService donationQueryService,
        UserProfileService userProfileService
    ) {
        this.donationService = donationService;
        this.donationRepository = donationRepository;
        this.donationQueryService = donationQueryService;
        this.userProfileService = userProfileService;
    }

    /**
     * {@code POST  /donations} : Create a new donation.
     *
     * @param donationDTO the donationDTO to create.
     * @return the {@link ResponseEntity} with status {@code 201 (Created)} and with body the new donationDTO, or with status {@code 400 (Bad Request)} if the donation has already an ID.
     * @throws URISyntaxException if the Location URI syntax is incorrect.
     */
    @PostMapping("")
    public ResponseEntity<DonationDTO> createDonation(@Valid @RequestBody DonationDTO donationDTO) throws URISyntaxException {
        LOG.debug("REST request to save Donation : {}", donationDTO);
        if (donationDTO.getId() != null) {
            throw new BadRequestAlertException("A new donation cannot already have an ID", ENTITY_NAME, "idexists");
        }
        donationDTO = donationService.save(donationDTO);
        return ResponseEntity.created(new URI("/api/donations/" + donationDTO.getId()))
            .headers(HeaderUtil.createEntityCreationAlert(applicationName, true, ENTITY_NAME, donationDTO.getId().toString()))
            .body(donationDTO);
    }

    /**
     * {@code PUT  /donations/:id} : Updates an existing donation.
     *
     * @param id the id of the donationDTO to save.
     * @param donationDTO the donationDTO to update.
     * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the updated donationDTO,
     * or with status {@code 400 (Bad Request)} if the donationDTO is not valid,
     * or with status {@code 500 (Internal Server Error)} if the donationDTO couldn't be updated.
     * @throws URISyntaxException if the Location URI syntax is incorrect.
     */
    @PutMapping("/{id}")
    public ResponseEntity<DonationDTO> updateDonation(
        @PathVariable(value = "id", required = false) final Long id,
        @Valid @RequestBody DonationDTO donationDTO
    ) throws URISyntaxException {
        LOG.debug("REST request to update Donation : {}, {}", id, donationDTO);
        if (donationDTO.getId() == null) {
            throw new BadRequestAlertException("Invalid id", ENTITY_NAME, "idnull");
        }
        if (!Objects.equals(id, donationDTO.getId())) {
            throw new BadRequestAlertException("Invalid ID", ENTITY_NAME, "idinvalid");
        }

        if (!donationRepository.existsById(id)) {
            throw new BadRequestAlertException("Entity not found", ENTITY_NAME, "idnotfound");
        }

        donationDTO = donationService.update(donationDTO);
        return ResponseEntity.ok()
            .headers(HeaderUtil.createEntityUpdateAlert(applicationName, true, ENTITY_NAME, donationDTO.getId().toString()))
            .body(donationDTO);
    }

    /**
     * {@code PATCH  /donations/:id} : Partial updates given fields of an existing donation, field will ignore if it is null
     *
     * @param id the id of the donationDTO to save.
     * @param donationDTO the donationDTO to update.
     * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the updated donationDTO,
     * or with status {@code 400 (Bad Request)} if the donationDTO is not valid,
     * or with status {@code 404 (Not Found)} if the donationDTO is not found,
     * or with status {@code 500 (Internal Server Error)} if the donationDTO couldn't be updated.
     * @throws URISyntaxException if the Location URI syntax is incorrect.
     */
    @PatchMapping(value = "/{id}", consumes = { "application/json", "application/merge-patch+json" })
    public ResponseEntity<DonationDTO> partialUpdateDonation(
        @PathVariable(value = "id", required = false) final Long id,
        @NotNull @RequestBody DonationDTO donationDTO
    ) throws URISyntaxException {
        LOG.debug("REST request to partial update Donation partially : {}, {}", id, donationDTO);
        if (donationDTO.getId() == null) {
            throw new BadRequestAlertException("Invalid id", ENTITY_NAME, "idnull");
        }
        if (!Objects.equals(id, donationDTO.getId())) {
            throw new BadRequestAlertException("Invalid ID", ENTITY_NAME, "idinvalid");
        }

        if (!donationRepository.existsById(id)) {
            throw new BadRequestAlertException("Entity not found", ENTITY_NAME, "idnotfound");
        }

        Optional<DonationDTO> result = donationService.partialUpdate(donationDTO);

        return ResponseUtil.wrapOrNotFound(
            result,
            HeaderUtil.createEntityUpdateAlert(applicationName, true, ENTITY_NAME, donationDTO.getId().toString())
        );
    }

    /**
     * {@code GET  /donations} : get all the donations.
     *
     * @param pageable the pagination information.
     * @param criteria the criteria which the requested entities should match.
     * @return the {@link ResponseEntity} with status {@code 200 (OK)} and the list of donations in body.
     */
    @GetMapping("")
    public ResponseEntity<List<DonationDTO>> getAllDonations(
        DonationCriteria criteria,
        @org.springdoc.core.annotations.ParameterObject Pageable pageable
    ) {
        LOG.debug("REST request to get Donations by criteria: {}", criteria);

        Page<DonationDTO> page = donationQueryService.findByCriteria(criteria, pageable);
        HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page);
        return ResponseEntity.ok().headers(headers).body(page.getContent());
    }

    /**
     * {@code GET  /donations/count} : count all the donations.
     *
     * @param criteria the criteria which the requested entities should match.
     * @return the {@link ResponseEntity} with status {@code 200 (OK)} and the count in body.
     */
    @GetMapping("/count")
    public ResponseEntity<Long> countDonations(DonationCriteria criteria) {
        LOG.debug("REST request to count Donations by criteria: {}", criteria);
        return ResponseEntity.ok().body(donationQueryService.countByCriteria(criteria));
    }

    /**
     * {@code GET  /donations/:id} : get the "id" donation.
     *
     * @param id the id of the donationDTO to retrieve.
     * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the donationDTO, or with status {@code 404 (Not Found)}.
     */
    @GetMapping("/{id}")
    public ResponseEntity<DonationDTO> getDonation(@PathVariable("id") Long id) {
        LOG.debug("REST request to get Donation : {}", id);
        Optional<DonationDTO> donationDTO = donationService.findOne(id);
        return ResponseUtil.wrapOrNotFound(donationDTO);
    }

    /**
     * {@code DELETE  /donations/:id} : delete the "id" donation.
     *
     * @param id the id of the donationDTO to delete.
     * @return the {@link ResponseEntity} with status {@code 204 (NO_CONTENT)}.
     */
    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteDonation(@PathVariable("id") Long id) {
        LOG.debug("REST request to delete Donation : {}", id);
        donationService.delete(id);
        return ResponseEntity.noContent()
            .headers(HeaderUtil.createEntityDeletionAlert(applicationName, true, ENTITY_NAME, id.toString()))
            .build();
    }

    /**
     * {@code POST  /donations/create-payment-intent} : Create a payment intent for current user.
     */
    @PostMapping("/create-payment-intent")
    public ResponseEntity<DonationPaymentIntentVM> createPaymentIntent(@Valid @RequestBody DonationCreatePaymentIntentDTO request) {
        LOG.debug("REST request to create payment intent: {}", request);

        String clientSecret = donationService.createDonationPaymentIntentForCurrentUser(request);
        DonationPaymentIntentVM response = new DonationPaymentIntentVM(clientSecret);

        return ResponseEntity.ok(response);
    }

    /**
     * {@code POST  /donations/process-donation} : Create payment intent and pending donation record for current user.
     */
    @PostMapping("/process-donation")
    public ResponseEntity<Map<String, Object>> processDonation(@Valid @RequestBody DonationCreatePaymentIntentDTO request) {
        LOG.debug("REST request to process donation: {}", request);

        try {
            Map<String, Object> result = donationService.createDonationPaymentIntentWithRecordForCurrentUser(request);
            Map<String, Object> response = Map.of(
                "success", true,
                "message", "Payment intent created successfully",
                "clientSecret", result.get("clientSecret"),
                "donation", result.get("donation")
            );
            return ResponseEntity.ok(response);
        } catch (Exception e) {
            LOG.error("Failed to process donation: {}", e.getMessage(), e);
            Map<String, Object> response = Map.of(
                "success", false,
                "message", e.getMessage()
            );
            return ResponseEntity.badRequest().body(response);
        }
    }

    /**
     * {@code POST  /donations/confirm-payment} : Confirm payment for current user.
     */
    @PostMapping("/confirm-payment")
    public ResponseEntity<DonationDTO> confirmPayment(@Valid @RequestBody DonationConfirmPaymentDTO request) {
        LOG.debug("REST request to confirm payment: {}", request);

        DonationDTO donationDTO = donationService.confirmDonationForCurrentUser(request);

        return ResponseEntity.ok(donationDTO);
    }

    /**
     * {@code GET  /donations/my-donations} : Get current user's donations.
     */
    @GetMapping("/my-donations")
    public ResponseEntity<Page<DonationDTO>> getCurrentUserDonations(Pageable pageable) {
        LOG.debug("REST request to get current user's donations");

        Page<DonationDTO> page = donationService.findByCurrentUser(pageable);
        HttpHeaders headers = new HttpHeaders();
        headers.add("x-total-count", String.valueOf(page.getTotalElements()));
        return ResponseEntity.ok().headers(headers).body(page);
    }
}
