package com.saas.tenant.service;

import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.calendar.CalendarScopes;
import com.saas.tenant.entity.CalendarIntegration;
import com.saas.tenant.repository.CalendarIntegrationRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import jakarta.annotation.PostConstruct;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.Optional;

@Service
@RequiredArgsConstructor
@Slf4j
public class CalendarAuthService {

    private final CalendarIntegrationRepository repository;

    @Value("${google.client.id}")
    private String clientId;

    @Value("${google.client.secret}")
    private String clientSecret;

    @Value("${google.client.redirect-uri}")
    private String redirectUri;

    private static final JsonFactory JSON_Factory = GsonFactory.getDefaultInstance();
    private GoogleAuthorizationCodeFlow flow;

    @PostConstruct
    public void init() {
        if (clientId == null || clientSecret == null) {
            log.warn("Google Client ID/Secret not configured. Calendar integration will not work.");
            return;
        }

        GoogleClientSecrets.Details details = new GoogleClientSecrets.Details();
        details.setClientId(clientId);
        details.setClientSecret(clientSecret);

        GoogleClientSecrets secrets = new GoogleClientSecrets();
        secrets.setWeb(details);

        flow = new GoogleAuthorizationCodeFlow.Builder(
                new NetHttpTransport(),
                JSON_Factory,
                secrets,
                Collections.singletonList(CalendarScopes.CALENDAR))
                .setAccessType("offline")
                .setApprovalPrompt("force") // Force to get refresh token
                .build();
    }

    public String getAuthorizationUrl() {
        // Fallback to configured URI if needed, but prefer dynamic
        return getAuthorizationUrl(redirectUri, null);
    }

    public String getAuthorizationUrl(String dynamicRedirectUri) {
        return getAuthorizationUrl(dynamicRedirectUri, null);
    }

    public String getAuthorizationUrl(String dynamicRedirectUri, String tenantId) {
        if (flow == null)
            throw new IllegalStateException("Google Calendar not configured");

        var urlBuilder = flow.newAuthorizationUrl()
                .setRedirectUri(dynamicRedirectUri);

        // Pass tenant ID as state parameter to preserve it through OAuth flow
        if (tenantId != null && !tenantId.isEmpty()) {
            urlBuilder.setState(tenantId);
            log.info("🔐 [CalendarAuthService] Adding tenant '{}' to OAuth state parameter", tenantId);
        }

        return urlBuilder.build();
    }

    public void exchangeCodeForToken(String code) throws IOException {
        exchangeCodeForToken(code, redirectUri);
    }

    public void exchangeCodeForToken(String code, String dynamicRedirectUri) throws IOException {
        if (flow == null)
            throw new IllegalStateException("Google Calendar not configured");

        log.info("🔄 [CalendarAuthService] Exchanging code for token with redirect_uri: {}", dynamicRedirectUri);
        log.debug("🔑 [CalendarAuthService] Authorization code: {}",
                code.substring(0, Math.min(20, code.length())) + "...");

        GoogleTokenResponse response = flow.newTokenRequest(code)
                .setRedirectUri(dynamicRedirectUri)
                .execute();

        String accessToken = response.getAccessToken();
        String refreshToken = response.getRefreshToken();
        Long expiresInSeconds = response.getExpiresInSeconds();

        LocalDateTime expiry = LocalDateTime.now().plusSeconds(expiresInSeconds != null ? expiresInSeconds : 3600);

        // Update or Create integration record
        Optional<CalendarIntegration> existing = repository.findByProvider(CalendarIntegration.Provider.GOOGLE);

        CalendarIntegration integration;
        if (existing.isPresent()) {
            integration = existing.get();
            integration.setAccessToken(accessToken);
            if (refreshToken != null) {
                integration.setRefreshToken(refreshToken);
            }
            integration.setTokenExpiry(expiry);
            integration.setIsActive(true);
        } else {
            integration = CalendarIntegration.builder()
                    .provider(CalendarIntegration.Provider.GOOGLE)
                    .accessToken(accessToken)
                    .refreshToken(refreshToken)
                    .tokenExpiry(expiry)
                    .calendarId("primary")
                    .isActive(true)
                    .build();
        }

        repository.save(integration);
        log.info("Successfully connected Google Calendar");
    }

    public boolean isConnected() {
        return repository.findByProvider(CalendarIntegration.Provider.GOOGLE)
                .map(CalendarIntegration::getIsActive)
                .orElse(false);
    }

    public boolean isConnected(String provider) {
        try {
            CalendarIntegration.Provider prov = CalendarIntegration.Provider.valueOf(provider.toUpperCase());
            return repository.findByProvider(prov)
                    .map(CalendarIntegration::getIsActive)
                    .orElse(false);
        } catch (IllegalArgumentException e) {
            log.warn("Unknown provider: {}", provider);
            return false;
        }
    }

    /**
     * Check if a specific doctor has calendar connected
     */
    public boolean isConnectedForDoctor(Long doctorId, String provider) {
        try {
            CalendarIntegration.Provider prov = CalendarIntegration.Provider.valueOf(provider.toUpperCase());
            return repository.findByDoctorIdAndProvider(doctorId, prov)
                    .map(CalendarIntegration::getIsActive)
                    .orElse(false);
        } catch (IllegalArgumentException e) {
            log.warn("Unknown provider: {}", provider);
            return false;
        }
    }

    public void disconnect() {
        repository.findByProvider(CalendarIntegration.Provider.GOOGLE)
                .ifPresent(integration -> {
                    integration.setIsActive(false);
                    integration.setAccessToken(null);
                    integration.setRefreshToken(null);
                    repository.save(integration);
                });
    }
}
