import { Controller, Get, Post, Put, Body, HttpCode, HttpStatus, Param, Query, Inject } from '@nestjs/common';
import { ApiTags, ApiResponse, ApiOperation } from '@nestjs/swagger';
import { Public } from '../auth/Public';
import { RequireAuthenticatedUser } from '../auth/RequireAuthenticatedUser';
import { RequireRoles } from '../auth/RequireRoles';
import { RequireCapability } from '../policy/RequireCapability';
import { SponsorService } from './SponsorService';
import { GetEntitySponsorshipPricingResultDTO } from './dtos/GetEntitySponsorshipPricingResultDTO';
import { GetSponsorsOutputDTO } from './dtos/GetSponsorsOutputDTO';
import { CreateSponsorInputDTO } from './dtos/CreateSponsorInputDTO';
import { CreateSponsorOutputDTO } from './dtos/CreateSponsorOutputDTO';
import { GetSponsorDashboardQueryParamsDTO } from './dtos/GetSponsorDashboardQueryParamsDTO';
import { SponsorDashboardDTO } from './dtos/SponsorDashboardDTO';
import { GetSponsorSponsorshipsQueryParamsDTO } from './dtos/GetSponsorSponsorshipsQueryParamsDTO';
import { SponsorSponsorshipsDTO } from './dtos/SponsorSponsorshipsDTO';
import { GetSponsorOutputDTO } from './dtos/GetSponsorOutputDTO';
import { GetPendingSponsorshipRequestsOutputDTO } from './dtos/GetPendingSponsorshipRequestsOutputDTO';
import { AcceptSponsorshipRequestInputDTO } from './dtos/AcceptSponsorshipRequestInputDTO';
import { RejectSponsorshipRequestInputDTO } from './dtos/RejectSponsorshipRequestInputDTO';
import { PaymentMethodDTO } from './dtos/PaymentMethodDTO';
import { InvoiceDTO } from './dtos/InvoiceDTO';
import { BillingStatsDTO } from './dtos/BillingStatsDTO';
import { AvailableLeagueDTO } from './dtos/AvailableLeagueDTO';
import { LeagueDetailDTO } from './dtos/LeagueDetailDTO';
import { SponsorDriverDTO } from './dtos/SponsorDriverDTO';
import { SponsorRaceDTO } from './dtos/RaceDTO';
import { SponsorProfileDTO } from './dtos/SponsorProfileDTO';
import { NotificationSettingsDTO } from './dtos/NotificationSettingsDTO';
import { PrivacySettingsDTO } from './dtos/PrivacySettingsDTO';
import type { AcceptSponsorshipRequestResultViewModel } from './presenters/AcceptSponsorshipRequestPresenter';
import type { RejectSponsorshipRequestResult } from '@core/racing/application/use-cases/RejectSponsorshipRequestUseCase';

@ApiTags('sponsors')
@Controller('sponsors')
export class SponsorController {
  constructor(@Inject(SponsorService) private readonly sponsorService: SponsorService) {}

  @Public()
  @Get('pricing')
  @ApiOperation({ summary: 'Get sponsorship pricing for an entity' })
  @ApiResponse({
    status: 200,
    description: 'Sponsorship pricing',
    type: GetEntitySponsorshipPricingResultDTO,
  })
  async getEntitySponsorshipPricing(): Promise<GetEntitySponsorshipPricingResultDTO> {
    return await this.sponsorService.getEntitySponsorshipPricing();
  }

  @Get()
  @RequireAuthenticatedUser()
  @RequireRoles('admin')
  @RequireCapability('sponsors.portal', 'view')
  @ApiOperation({ summary: 'Get all sponsors' })
  @ApiResponse({
    status: 200,
    description: 'List of sponsors',
    type: GetSponsorsOutputDTO,
  })
  async getSponsors(): Promise<GetSponsorsOutputDTO> {
    return await this.sponsorService.getSponsors();
  }

  @Post()
  @RequireAuthenticatedUser()
  @RequireRoles('admin')
  @RequireCapability('sponsors.portal', 'mutate')
  @HttpCode(HttpStatus.CREATED)
  @ApiOperation({ summary: 'Create a new sponsor' })
  @ApiResponse({
    status: 201,
    description: 'Sponsor created',
    type: CreateSponsorOutputDTO,
  })
  async createSponsor(@Body() input: CreateSponsorInputDTO): Promise<CreateSponsorOutputDTO> {
    return await this.sponsorService.createSponsor(input);
  }

  @Get('dashboard/:sponsorId')
  @RequireAuthenticatedUser()
  @RequireRoles('admin')
  @RequireCapability('sponsors.portal', 'view')
  @ApiOperation({ summary: 'Get sponsor dashboard metrics and sponsored leagues' })
  @ApiResponse({
    status: 200,
    description: 'Sponsor dashboard data',
    type: SponsorDashboardDTO,
  })
  @ApiResponse({ status: 404, description: 'Sponsor not found' })
  async getSponsorDashboard(
    @Param('sponsorId') sponsorId: string,
  ): Promise<SponsorDashboardDTO> {
    return await this.sponsorService.getSponsorDashboard({
      sponsorId,
    } as GetSponsorDashboardQueryParamsDTO);
  }

  @Get(':sponsorId/sponsorships')
  @RequireAuthenticatedUser()
  @RequireRoles('admin')
  @RequireCapability('sponsors.portal', 'view')
  @ApiOperation({
    summary: 'Get all sponsorships for a given sponsor',
  })
  @ApiResponse({
    status: 200,
    description: 'List of sponsorships',
    type: SponsorSponsorshipsDTO,
  })
  @ApiResponse({ status: 404, description: 'Sponsor not found' })
  async getSponsorSponsorships(
    @Param('sponsorId') sponsorId: string,
  ): Promise<SponsorSponsorshipsDTO> {
    return await this.sponsorService.getSponsorSponsorships({
      sponsorId,
    } as GetSponsorSponsorshipsQueryParamsDTO);
  }

  @Get('requests')
  @RequireAuthenticatedUser()
  @RequireRoles('admin')
  @RequireCapability('sponsors.portal', 'view')
  @ApiOperation({ summary: 'Get pending sponsorship requests' })
  @ApiResponse({
    status: 200,
    description: 'List of pending sponsorship requests',
    type: GetPendingSponsorshipRequestsOutputDTO,
  })
  async getPendingSponsorshipRequests(
    @Query() query: { entityType: string; entityId: string },
  ): Promise<GetPendingSponsorshipRequestsOutputDTO> {
    return await this.sponsorService.getPendingSponsorshipRequests(
      query as {
        entityType: import('@core/racing/domain/entities/SponsorshipRequest').SponsorableEntityType;
        entityId: string;
      },
    );
  }

  @Get(':sponsorId')
  @RequireAuthenticatedUser()
  @RequireRoles('admin')
  @RequireCapability('sponsors.portal', 'view')
  @ApiOperation({ summary: 'Get a sponsor by ID' })
  @ApiResponse({
    status: 200,
    description: 'Sponsor data',
    type: GetSponsorOutputDTO,
  })
  @ApiResponse({ status: 404, description: 'Sponsor not found' })
  async getSponsor(@Param('sponsorId') sponsorId: string): Promise<GetSponsorOutputDTO> {
    return await this.sponsorService.getSponsor(sponsorId);
  }

  @Post('requests/:requestId/accept')
  @RequireAuthenticatedUser()
  @RequireRoles('admin')
  @RequireCapability('sponsors.portal', 'mutate')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Accept a sponsorship request' })
  @ApiResponse({ status: 200, description: 'Sponsorship request accepted' })
  @ApiResponse({ status: 400, description: 'Invalid request' })
  @ApiResponse({ status: 404, description: 'Request not found' })
  async acceptSponsorshipRequest(
    @Param('requestId') requestId: string,
    @Body() input: AcceptSponsorshipRequestInputDTO,
  ): Promise<AcceptSponsorshipRequestResultViewModel | null> {
    return await this.sponsorService.acceptSponsorshipRequest(
      requestId,
      input.respondedBy,
    );
  }

  @Post('requests/:requestId/reject')
  @RequireAuthenticatedUser()
  @RequireRoles('admin')
  @RequireCapability('sponsors.portal', 'mutate')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Reject a sponsorship request' })
  @ApiResponse({ status: 200, description: 'Sponsorship request rejected' })
  @ApiResponse({ status: 400, description: 'Invalid request' })
  @ApiResponse({ status: 404, description: 'Request not found' })
  async rejectSponsorshipRequest(
    @Param('requestId') requestId: string,
    @Body() input: RejectSponsorshipRequestInputDTO,
  ): Promise<RejectSponsorshipRequestResult | null> {
    return await this.sponsorService.rejectSponsorshipRequest(
      requestId,
      input.respondedBy,
      input.reason,
    );
  }

  @Get('billing/:sponsorId')
  @RequireAuthenticatedUser()
  @RequireRoles('admin')
  @RequireCapability('sponsors.portal', 'view')
  @ApiOperation({ summary: 'Get sponsor billing information' })
  @ApiResponse({ status: 200, description: 'Sponsor billing data', type: Object })
  async getSponsorBilling(
    @Param('sponsorId') sponsorId: string,
  ): Promise<{
    paymentMethods: PaymentMethodDTO[];
    invoices: InvoiceDTO[];
    stats: BillingStatsDTO;
  }> {
    return await this.sponsorService.getSponsorBilling(sponsorId);
  }

  @Get('leagues/available')
  @RequireAuthenticatedUser()
  @RequireRoles('admin')
  @RequireCapability('sponsors.portal', 'view')
  @ApiOperation({ summary: 'Get available leagues for sponsorship' })
  @ApiResponse({
    status: 200,
    description: 'Available leagues',
    type: [AvailableLeagueDTO],
  })
  async getAvailableLeagues(): Promise<AvailableLeagueDTO[] | null> {
    const presenter = await this.sponsorService.getAvailableLeagues();
    return presenter.viewModel;
  }

  @Get('leagues/:leagueId/detail')
  @RequireAuthenticatedUser()
  @RequireRoles('admin')
  @RequireCapability('sponsors.portal', 'view')
  @ApiOperation({ summary: 'Get detailed league information for sponsors' })
  @ApiResponse({ status: 200, description: 'League detail data', type: Object })
  async getLeagueDetail(
    @Param('leagueId') leagueId: string,
  ): Promise<{
    league: LeagueDetailDTO;
    drivers: SponsorDriverDTO[];
    races: SponsorRaceDTO[];
  } | null> {
    const presenter = await this.sponsorService.getLeagueDetail(leagueId);
    return presenter.viewModel;
  }

  @Get('settings/:sponsorId')
  @RequireAuthenticatedUser()
  @RequireRoles('admin')
  @RequireCapability('sponsors.portal', 'view')
  @ApiOperation({ summary: 'Get sponsor settings' })
  @ApiResponse({ status: 200, description: 'Sponsor settings', type: Object })
  async getSponsorSettings(
    @Param('sponsorId') sponsorId: string,
  ): Promise<{
    profile: SponsorProfileDTO;
    notifications: NotificationSettingsDTO;
    privacy: PrivacySettingsDTO;
  } | null> {
    const presenter = await this.sponsorService.getSponsorSettings(sponsorId);
    return presenter.viewModel;
  }

  @Put('settings/:sponsorId')
  @RequireAuthenticatedUser()
  @RequireRoles('admin')
  @RequireCapability('sponsors.portal', 'mutate')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Update sponsor settings' })
  @ApiResponse({ status: 200, description: 'Settings updated successfully' })
  async updateSponsorSettings(
    @Param('sponsorId') sponsorId: string,
    @Body()
    input: {
      profile?: Partial<SponsorProfileDTO>;
      notifications?: Partial<NotificationSettingsDTO>;
      privacy?: Partial<PrivacySettingsDTO>;
    },
  ): Promise<{ success: boolean; errorCode?: string; message?: string } | null> {
    const presenter = await this.sponsorService.updateSponsorSettings(sponsorId, input);
    return presenter.viewModel;
  }
}
