package pl.polskalokalnie.auth;

import java.time.LocalDate;
import java.time.format.DateTimeParseException;
import java.util.Locale;
import org.springframework.http.HttpStatus;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;
import pl.polskalokalnie.moderation.TextModerationService;
import pl.polskalokalnie.auth.dto.AuthResponse;
import pl.polskalokalnie.auth.dto.LoginRequest;
import pl.polskalokalnie.auth.dto.RegisterRequest;
import pl.polskalokalnie.auth.dto.SocialLoginRequest;
import pl.polskalokalnie.auth.dto.UpdateProfileRequest;
import pl.polskalokalnie.auth.dto.UserResponse;
import pl.polskalokalnie.user.AccountType;
import pl.polskalokalnie.user.AppUser;
import pl.polskalokalnie.user.AuthProvider;
import pl.polskalokalnie.user.BlockedEmailRepository;
import pl.polskalokalnie.user.ContactPreference;
import pl.polskalokalnie.user.PreferredLanguage;
import pl.polskalokalnie.user.Role;
import pl.polskalokalnie.user.UserRepository;

@Service
public class AuthService {

    private final UserRepository userRepository;
    private final BlockedEmailRepository blockedEmailRepository;
    private final PasswordEncoder passwordEncoder;
    private final JwtService jwtService;
    private final TextModerationService textModerationService;

    public AuthService(
            UserRepository userRepository,
            BlockedEmailRepository blockedEmailRepository,
            PasswordEncoder passwordEncoder,
            JwtService jwtService,
            TextModerationService textModerationService
    ) {
        this.userRepository = userRepository;
        this.blockedEmailRepository = blockedEmailRepository;
        this.passwordEncoder = passwordEncoder;
        this.jwtService = jwtService;
        this.textModerationService = textModerationService;
    }

    public AuthResponse register(RegisterRequest request) {
        String email = normalizeEmail(request.email());
        if (blockedEmailRepository.existsByEmailIgnoreCase(email)) {
            throw new ResponseStatusException(HttpStatus.FORBIDDEN, "Ten adres e-mail został zablokowany i nie można go już użyć do rejestracji");
        }
        if (userRepository.existsByEmailIgnoreCase(email)) {
            throw new ResponseStatusException(HttpStatus.CONFLICT, "Konto z tym adresem e-mail już istnieje");
        }

        textModerationService.validateOrThrow(request.fullName(), request.phone(), request.nip());

        AppUser user = new AppUser();
        user.setEmail(email);
        user.setFullName(request.fullName().trim());
        user.setPasswordHash(passwordEncoder.encode(request.password()));
        user.setRole(Role.USER);
        user.setProvider(AuthProvider.LOCAL);
        user.setAccountType(request.accountType() != null ? request.accountType() : AccountType.PERSONAL);
        user.setPhone(request.phone() != null && !request.phone().isBlank() ? request.phone().trim() : null);
        user.setNip(request.nip() != null && !request.nip().isBlank() ? request.nip().trim() : null);
        // Konta zalozone samodzielnie czekaja na weryfikacje danych przez administratora.
        user.setVerified(false);

        return buildAuthResponse(userRepository.save(user));
    }

    public AuthResponse login(LoginRequest request) {
        AppUser user = userRepository.findByEmailIgnoreCase(normalizeEmail(request.email()))
                .orElseThrow(() -> new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Nieprawidłowy e-mail lub hasło"));

        if (user.getPasswordHash() == null
                || !passwordEncoder.matches(request.password(), user.getPasswordHash())) {
            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Nieprawidłowy e-mail lub hasło");
        }
        if (user.isBlocked()) {
            throw new ResponseStatusException(HttpStatus.FORBIDDEN, "Konto zostało zablokowane");
        }

        return buildAuthResponse(user);
    }

    /**
     * Symulowane logowanie spoleczne: znajduje lub tworzy konto powiazane z dostawca.
     * Nie ma tu prawdziwego OAuth - dane przychodza z frontendu jako demo.
     */
    public AuthResponse socialLogin(SocialLoginRequest request) {
        AuthProvider provider = request.provider();
        String email = normalizeEmail(
                request.email() != null && !request.email().isBlank()
                        ? request.email()
                        : defaultSocialEmail(provider));
        String name = request.fullName() != null && !request.fullName().isBlank()
                ? request.fullName().trim()
                : defaultSocialName(provider);

        textModerationService.validateOrThrow(name);

        AppUser user = userRepository.findByEmailIgnoreCase(email).orElseGet(() -> {
            if (blockedEmailRepository.existsByEmailIgnoreCase(email)) {
                throw new ResponseStatusException(HttpStatus.FORBIDDEN, "Ten adres e-mail został zablokowany i nie można go już użyć do rejestracji");
            }
            AppUser created = new AppUser();
            created.setEmail(email);
            created.setFullName(name);
            created.setRole(Role.USER);
            created.setProvider(provider);
            // Logowanie spoleczne nie wymaga rekopisania danych, wiec konto jest od razu zweryfikowane.
            created.setVerified(true);
            return userRepository.save(created);
        });

        if (user.isBlocked()) {
            throw new ResponseStatusException(HttpStatus.FORBIDDEN, "Konto zostało zablokowane");
        }

        return buildAuthResponse(user);
    }

    public UserResponse currentUser(String email) {
        return userRepository.findByEmailIgnoreCase(email)
                .map(UserResponse::from)
                .orElseThrow(() -> new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Sesja wygasła"));
    }

    public UserResponse updateProfile(String email, UpdateProfileRequest request) {
        AppUser user = userRepository.findByEmailIgnoreCase(email)
                .orElseThrow(() -> new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Sesja wygasła"));

        textModerationService.validateOrThrow(request.fullName(), request.phone(), request.birthDate(), request.address());

        user.setFullName(request.fullName().trim());
        user.setPhone(request.phone() != null && !request.phone().isBlank() ? request.phone().trim() : null);
        user.setAddress(request.address() != null && !request.address().isBlank() ? request.address().trim() : null);
        user.setContactPreference(request.contactPreference() != null ? request.contactPreference() : ContactPreference.EMAIL_AND_PHONE);
        user.setPreferredLanguage(request.preferredLanguage() != null ? request.preferredLanguage() : PreferredLanguage.PL);
        if (request.birthDate() != null && !request.birthDate().isBlank()) {
            try {
                user.setBirthDate(LocalDate.parse(request.birthDate().trim()));
            } catch (DateTimeParseException ex) {
                throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Nieprawidłowy format daty urodzenia");
            }
        } else {
            user.setBirthDate(null);
        }

        return UserResponse.from(userRepository.save(user));
    }

    private AuthResponse buildAuthResponse(AppUser user) {
        return new AuthResponse(jwtService.generateToken(user), UserResponse.from(user));
    }

    private String normalizeEmail(String email) {
        return email.trim().toLowerCase(Locale.ROOT);
    }

    private String defaultSocialEmail(AuthProvider provider) {
        return switch (provider) {
            case GOOGLE -> "demo.google@gmail.com";
            case FACEBOOK -> "demo.facebook@facebook.com";
            case LOCAL -> "demo.local@mieszko.pl";
        };
    }

    private String defaultSocialName(AuthProvider provider) {
        return switch (provider) {
            case GOOGLE -> "Użytkownik Google";
            case FACEBOOK -> "Użytkownik Facebook";
            case LOCAL -> "Użytkownik";
        };
    }
}
