package com.flightticket.controller;

import com.flightticket.dto.auth.*;
import com.flightticket.dto.error.ErrorResponse;
import com.flightticket.service.AuthenticationService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@Slf4j
@RestController
@RequestMapping("/api/v1/auth")
@RequiredArgsConstructor
@Tag(name = "Authentication", description = "Authentication and authorization endpoints including login, logout, token refresh, and MFA management")
public class AuthenticationController {
    
    private final AuthenticationService authenticationService;
    
    @Operation(
            summary = "User login",
            description = """
                    Authenticate user credentials and obtain JWT tokens.
                    
                    - For regular users (AGENT, HR_USER, FINANCE_USER): username and password are sufficient
                    - For admin users (SUPER_ADMIN, AGENCY_ADMIN): MFA code is required
                    - Returns access token (short-lived) and refresh token (long-lived)
                    - If MFA is required but not provided, returns mfaRequired=true
                    """,
            security = {}
    )
    @ApiResponses(value = {
            @ApiResponse(
                    responseCode = "200",
                    description = "Authentication successful",
                    content = @Content(
                            mediaType = "application/json",
                            schema = @Schema(implementation = AuthenticationResponse.class),
                            examples = @ExampleObject(value = """
                                    {
                                      "accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
                                      "refreshToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
                                      "expiresIn": 3600,
                                      "userInfo": {
                                        "userId": "123e4567-e89b-12d3-a456-426614174000",
                                        "username": "john.doe",
                                        "email": "john.doe@agency.com",
                                        "roles": ["AGENT"],
                                        "agencyId": "987e6543-e21b-12d3-a456-426614174000"
                                      },
                                      "mfaRequired": false
                                    }
                                    """)
                    )
            ),
            @ApiResponse(
                    responseCode = "401",
                    description = "Invalid credentials or MFA code",
                    content = @Content(
                            mediaType = "application/json",
                            schema = @Schema(implementation = ErrorResponse.class)
                    )
            ),
            @ApiResponse(
                    responseCode = "403",
                    description = "Account locked or agency inactive",
                    content = @Content(
                            mediaType = "application/json",
                            schema = @Schema(implementation = ErrorResponse.class)
                    )
            )
    })
    @PostMapping("/login")
    public ResponseEntity<AuthenticationResponse> login(@Valid @RequestBody LoginRequest request) {
        log.info("Login request received for user: {}", request.getUsername());
        AuthenticationResponse response = authenticationService.authenticate(request);
        return ResponseEntity.ok(response);
    }
    
    @Operation(
            summary = "Refresh access token",
            description = """
                    Obtain a new access token using a valid refresh token.
                    
                    - Refresh tokens are long-lived (typically 7 days)
                    - Access tokens are short-lived (typically 1 hour)
                    - Use this endpoint to get a new access token without re-authenticating
                    """,
            security = {}
    )
    @ApiResponses(value = {
            @ApiResponse(
                    responseCode = "200",
                    description = "Token refreshed successfully",
                    content = @Content(
                            mediaType = "application/json",
                            schema = @Schema(implementation = TokenResponse.class)
                    )
            ),
            @ApiResponse(
                    responseCode = "401",
                    description = "Invalid or expired refresh token",
                    content = @Content(
                            mediaType = "application/json",
                            schema = @Schema(implementation = ErrorResponse.class)
                    )
            )
    })
    @PostMapping("/refresh")
    public ResponseEntity<TokenResponse> refreshToken(@RequestBody String refreshToken) {
        log.info("Token refresh request received");
        TokenResponse response = authenticationService.refreshToken(refreshToken);
        return ResponseEntity.ok(response);
    }
    
    @Operation(
            summary = "User logout",
            description = """
                    Invalidate the current session and revoke tokens.
                    
                    - Invalidates both access and refresh tokens
                    - Clears session from Redis cache
                    - User must login again to obtain new tokens
                    """,
            security = @SecurityRequirement(name = "bearerAuth")
    )
    @ApiResponses(value = {
            @ApiResponse(
                    responseCode = "200",
                    description = "Logout successful"
            ),
            @ApiResponse(
                    responseCode = "401",
                    description = "Invalid or missing token",
                    content = @Content(
                            mediaType = "application/json",
                            schema = @Schema(implementation = ErrorResponse.class)
                    )
            )
    })
    @PostMapping("/logout")
    public ResponseEntity<Void> logout(@RequestBody String refreshToken) {
        log.info("Logout request received");
        authenticationService.logout(refreshToken);
        return ResponseEntity.ok().build();
    }
    
    @Operation(
            summary = "Setup MFA for user",
            description = """
                    Generate MFA secret and QR code for user to setup authenticator app.
                    
                    - Required for SUPER_ADMIN and AGENCY_ADMIN roles
                    - Returns QR code URL and secret key
                    - User must scan QR code with authenticator app (Google Authenticator, Authy, etc.)
                    - After setup, MFA code is required for login
                    """,
            security = @SecurityRequirement(name = "bearerAuth")
    )
    @ApiResponses(value = {
            @ApiResponse(
                    responseCode = "200",
                    description = "MFA setup initiated successfully",
                    content = @Content(
                            mediaType = "application/json",
                            schema = @Schema(implementation = MfaSetupResponse.class)
                    )
            ),
            @ApiResponse(
                    responseCode = "401",
                    description = "Unauthorized",
                    content = @Content(
                            mediaType = "application/json",
                            schema = @Schema(implementation = ErrorResponse.class)
                    )
            ),
            @ApiResponse(
                    responseCode = "404",
                    description = "User not found",
                    content = @Content(
                            mediaType = "application/json",
                            schema = @Schema(implementation = ErrorResponse.class)
                    )
            )
    })
    @PostMapping("/mfa/setup")
    public ResponseEntity<MfaSetupResponse> setupMfa(@RequestParam String userId) {
        log.info("MFA setup request for user: {}", userId);
        MfaSetupResponse response = authenticationService.setupMfa(userId);
        return ResponseEntity.ok(response);
    }
    
    @Operation(
            summary = "Verify MFA code",
            description = """
                    Verify a time-based one-time password (TOTP) from authenticator app.
                    
                    - Used during login for admin users
                    - Code is valid for 30 seconds
                    - Returns true if code is valid, false otherwise
                    """,
            security = @SecurityRequirement(name = "bearerAuth")
    )
    @ApiResponses(value = {
            @ApiResponse(
                    responseCode = "200",
                    description = "MFA verification result",
                    content = @Content(
                            mediaType = "application/json",
                            schema = @Schema(implementation = Boolean.class)
                    )
            ),
            @ApiResponse(
                    responseCode = "401",
                    description = "Unauthorized",
                    content = @Content(
                            mediaType = "application/json",
                            schema = @Schema(implementation = ErrorResponse.class)
                    )
            )
    })
    @PostMapping("/mfa/verify")
    public ResponseEntity<Boolean> verifyMfa(@Valid @RequestBody MfaVerificationRequest request) {
        log.info("MFA verification request for user: {}", request.getUserId());
        boolean verified = authenticationService.verifyMfa(request.getUserId(), request.getCode());
        return ResponseEntity.ok(verified);
    }
}
