package com.honey.honey.controller;

import com.honey.honey.dto.ReferralDto;
import com.honey.honey.dto.UserDto;
import com.honey.honey.model.UserA;
import com.honey.honey.model.UserB;
import com.honey.honey.repository.UserBRepository;
import com.honey.honey.security.UserContext;
import com.honey.honey.service.AvatarService;
import com.honey.honey.service.FeatureSwitchService;
import com.honey.honey.service.LocalizationService;
import com.honey.honey.service.UserService;
import com.honey.honey.util.IpUtils;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

@Slf4j
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {

    /** Divisor for converting balance bigint to display value (backend sends display value only). */
    private static final double BALANCE_DISPLAY_DIVISOR = 1_000_000.0;

    private final UserService userService;
    private final UserBRepository userBRepository;
    private final AvatarService avatarService;
    private final LocalizationService localizationService;
    private final FeatureSwitchService featureSwitchService;

    @GetMapping("/current")
    public UserDto getCurrentUser() {
        UserA user = UserContext.get();
        
        // Convert IP from byte[] to string for display
        String ipAddress = IpUtils.bytesToIp(user.getIp());
        
        // Get balances from UserB; convert to display value (divide by 1_000_000)
        var userBOpt = userBRepository.findById(user.getId());
        long rawBalanceA = userBOpt.map(UserB::getBalanceA).orElse(0L);
        long rawBalanceB = userBOpt.map(UserB::getBalanceB).orElse(0L);
        Double balanceA = rawBalanceA / BALANCE_DISPLAY_DIVISOR;
        Double balanceB = rawBalanceB / BALANCE_DISPLAY_DIVISOR;
        Integer depositCount = userBOpt.map(UserB::getDepositCount).orElse(0);
        
        // Generate avatar URL on-the-fly (deterministic from userId)
        String avatarUrl = avatarService.getAvatarUrl(user.getId());
        
        boolean banned = user.getBanned() != null && user.getBanned() != 0;
        
        return UserDto.builder()
                .id(user.getId())
                .telegram_id(user.getTelegramId())
                .username(user.getTelegramName())
                .screenName(user.getScreenName())
                .dateReg(user.getDateReg())
                .ip(ipAddress)
                .balanceA(balanceA)
                .balanceB(balanceB)
                .avatarUrl(avatarUrl)
                .languageCode(user.getLanguageCode())
                .paymentEnabled(featureSwitchService.isPaymentEnabled())
                .payoutEnabled(featureSwitchService.isPayoutEnabled())
                .banned(banned)
                .depositCount(depositCount)
                .build();
    }
    
    /**
     * Updates user's language code.
     * Called when user changes language in app header.
     */
    @PutMapping("/language")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void updateLanguage(@RequestBody UpdateLanguageRequest request) {
        UserA user = UserContext.get();
        userService.updateLanguageCode(user.getId(), request.getLanguageCode());
    }
    
    /**
     * Adds deposit amount to user's balance_a.
     * For now, this is a mock implementation that directly adds to balance.
     * Will be replaced with payment integration later.
     */
    @PostMapping("/deposit")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void deposit(@RequestBody DepositRequest request) {
        UserA user = UserContext.get();
        
        // Frontend sends amount already in bigint format (no conversion needed)
        Long depositAmount = request.getAmount();
        if (depositAmount == null || depositAmount <= 0) {
            throw new IllegalArgumentException(localizationService.getMessage("user.error.depositAmountInvalid"));
        }
        
        UserB userB = userBRepository.findById(user.getId())
                .orElseThrow(() -> new IllegalStateException(localizationService.getMessage("user.error.balanceNotFound")));
        
        // Add to balance
        userB.setBalanceA(userB.getBalanceA() + depositAmount);
        
        // Update deposit statistics
        userB.setDepositTotal(userB.getDepositTotal() + depositAmount);
        userB.setDepositCount(userB.getDepositCount() + 1);
        
        userBRepository.save(userB);
    }
    
    @Data
    public static class UpdateLanguageRequest {
        private String languageCode;
    }
    
    /**
     * Gets referrals for a specific level with pagination.
     * Returns 10 results per page, ordered by commission DESC, then id DESC.
     *
     * @param level The referral level (1, 2, or 3)
     * @param page Page number (0-indexed, defaults to 0)
     * @return Page of referrals with name and commission
     */
    @GetMapping("/referrals")
    public ReferralsResponse getReferrals(
            @RequestParam Integer level,
            @RequestParam(defaultValue = "0") Integer page) {
        UserA user = UserContext.get();
        
        Page<ReferralDto> referralsPage = userService.getReferrals(user.getId(), level, page);
        return new ReferralsResponse(
                referralsPage.getContent(),
                referralsPage.getNumber(),
                referralsPage.getTotalPages(),
                referralsPage.getTotalElements()
        );
    }
    
    @Data
    public static class DepositRequest {
        private Long amount; // Amount in bigint format (frontend converts before sending)
    }
    
    @Data
    @RequiredArgsConstructor
    public static class ReferralsResponse {
        private final java.util.List<ReferralDto> referrals;
        private final Integer currentPage;
        private final Integer totalPages;
        private final Long totalElements;
    }
}
