import { ApiProperty, OmitType } from '@nestjs/swagger';
import { Transform, Type } from 'class-transformer';
import {
    ArrayMaxSize, IsArray, IsEnum, IsNotEmpty, IsIn, IsInt, IsMongoId, IsNumber, IsOptional, IsString, Max, MaxLength, Min, MinLength, ValidateNested
} from 'class-validator';
import { UploadedFileDto } from 'src/common/dto/uploaded-file.dto';
import { InsuredRelation, PolicyDisplayStatus, PremiumFrequency } from '../schema/policy.schema';

const trim = ({ value }: { value: unknown }) => (typeof value === 'string' ? value.trim() : value);
const upperTrim = ({ value }: { value: unknown }) => (typeof value === 'string' ? value.trim().toUpperCase() : value);

export enum IssuedPeriod {
    LAST_30_DAYS = 'LAST_30_DAYS',
    LAST_90_DAYS = 'LAST_90_DAYS',
    THIS_QUARTER = 'THIS_QUARTER'
}

export class InsuredMemberDto {
    @ApiProperty({ example: 'Sneha Verma' })
    @IsString()
    @MinLength(2)
    @MaxLength(80)
    @Transform(trim)
    name: string;

    @ApiProperty({ enum: InsuredRelation })
    @IsEnum(InsuredRelation)
    relation: InsuredRelation;
}

export class CreatePolicyDto {
    @ApiProperty({ example: 'HDFC-MOT-2026-9901' })
    @IsString()
    @MinLength(3)
    @MaxLength(60)
    @Transform(upperTrim)
    policy_number: string;

    @ApiProperty({ description: 'Customer id' })
    @IsMongoId()
    customer_id: string;

    @ApiProperty({ description: 'Agent or Policy Staff who handled it. Agents need the product category approved; Policy Staff can only assign to themselves.' })
    @IsMongoId()
    assigned_to: string;

    @ApiProperty({ description: 'Active product; GST and commission are calculated from it' })
    @IsMongoId()
    product_id: string;

    @ApiProperty({ example: 15000, description: 'Gross premium including GST' })
    @IsNumber()
    @Min(0)
    gross_premium: number;

    @ApiProperty({ enum: PremiumFrequency, required: false, default: PremiumFrequency.ONE_TIME })
    @IsOptional()
    @IsEnum(PremiumFrequency)
    premium_frequency?: PremiumFrequency;

    @ApiProperty({ example: 1781481600000, description: 'Issue date, epoch milliseconds' })
    @IsInt()
    issue_date: number;

    @ApiProperty({ example: 1813017600000, description: 'Expiry date, epoch milliseconds, after the issue date' })
    @IsInt()
    expiry_date: number;

    @ApiProperty({ required: false, example: 1500000 })
    @IsOptional()
    @IsNumber()
    @Min(0)
    sum_insured?: number;

    @ApiProperty({ required: false, example: 'Floater' })
    @IsOptional()
    @IsString()
    @MaxLength(60)
    @Transform(trim)
    sum_insured_note?: string;

    @ApiProperty({ required: false, type: [InsuredMemberDto] })
    @IsOptional()
    @IsArray()
    @ArrayMaxSize(20)
    @ValidateNested({ each: true })
    @Type(() => InsuredMemberDto)
    insured_members?: InsuredMemberDto[];

    @ApiProperty({ required: false, example: 'MH-04-AB-1234', description: 'Motor policies' })
    @IsOptional()
    @IsString()
    @MaxLength(20)
    @Transform(upperTrim)
    vehicle_registration?: string;

    @ApiProperty({ required: false, type: UploadedFileDto, description: 'Policy schedule PDF, uploaded first with POST /uploads/file' })
    @IsOptional()
    @ValidateNested()
    @Type(() => UploadedFileDto)
    schedule_file?: UploadedFileDto;
}

// The customer comes from the lead. The assignee defaults to the lead's assignee.
export class ConvertLeadToPolicyDto extends OmitType(CreatePolicyDto, ['customer_id', 'assigned_to'] as const) {
    @ApiProperty({ required: false, description: 'Defaults to the lead\'s current assignee' })
    @IsOptional()
    @IsMongoId()
    assigned_to?: string;
}

export class UpdatePolicyDto {
    @ApiProperty({ required: false, description: 'Recalculates net premium, GST and commission from the rates saved on the policy' })
    @IsOptional()
    @IsNumber()
    @Min(0)
    gross_premium?: number;

    @ApiProperty({ required: false, enum: PremiumFrequency })
    @IsOptional()
    @IsEnum(PremiumFrequency)
    premium_frequency?: PremiumFrequency;

    @ApiProperty({ required: false })
    @IsOptional()
    @IsInt()
    issue_date?: number;

    @ApiProperty({ required: false })
    @IsOptional()
    @IsInt()
    expiry_date?: number;

    @ApiProperty({ required: false })
    @IsOptional()
    @IsNumber()
    @Min(0)
    sum_insured?: number;

    @ApiProperty({ required: false })
    @IsOptional()
    @IsString()
    @MaxLength(60)
    @Transform(trim)
    sum_insured_note?: string;

    @ApiProperty({ required: false, type: [InsuredMemberDto], description: 'Replaces the insured members' })
    @IsOptional()
    @IsArray()
    @ArrayMaxSize(20)
    @ValidateNested({ each: true })
    @Type(() => InsuredMemberDto)
    insured_members?: InsuredMemberDto[];

    @ApiProperty({ required: false })
    @IsOptional()
    @IsString()
    @MaxLength(20)
    @Transform(upperTrim)
    vehicle_registration?: string;

    @ApiProperty({ required: false, type: UploadedFileDto })
    @IsOptional()
    @ValidateNested()
    @Type(() => UploadedFileDto)
    schedule_file?: UploadedFileDto;
}

export class CancelPolicyDto {
    @ApiProperty({ example: 'Customer surrendered the policy' })
    @IsString()
    @MinLength(3)
    @MaxLength(300)
    @Transform(trim)
    @IsNotEmpty({ message: 'A cancellation reason is required' })
    reason: string;
}

export class PolicyListDto {
    @ApiProperty({ required: false, default: 1 })
    @IsOptional()
    @Transform(({ value }) => Number(value))
    @IsInt()
    @Min(1)
    page: number = 1;

    @ApiProperty({ required: false, default: 10 })
    @IsOptional()
    @Transform(({ value }) => Number(value))
    @IsInt()
    @Min(1)
    @Max(100)
    limit: number = 10;

    @ApiProperty({ required: false, description: 'Search by policy number, customer name or mobile, or insurer name' })
    @IsOptional()
    @IsString()
    search?: string;

    @ApiProperty({ required: false, description: 'Product category, the "All Products" filter' })
    @IsOptional()
    @IsMongoId()
    category_id?: string;

    @ApiProperty({ required: false })
    @IsOptional()
    @IsMongoId()
    product_id?: string;

    @ApiProperty({ required: false })
    @IsOptional()
    @IsMongoId()
    insurer_id?: string;

    @ApiProperty({ required: false, description: 'Agent or Policy Staff' })
    @IsOptional()
    @IsMongoId()
    assigned_to?: string;

    @ApiProperty({ required: false })
    @IsOptional()
    @IsMongoId()
    customer_id?: string;

    @ApiProperty({ required: false, enum: PolicyDisplayStatus })
    @IsOptional()
    @IsIn(Object.values(PolicyDisplayStatus))
    status?: PolicyDisplayStatus;

    @ApiProperty({ required: false, enum: IssuedPeriod })
    @IsOptional()
    @IsIn(Object.values(IssuedPeriod))
    issued?: IssuedPeriod;

    @ApiProperty({ required: false, example: 60, description: 'Active policies expiring within this many days (renewals)' })
    @IsOptional()
    @Transform(({ value }) => Number(value))
    @IsInt()
    @Min(1)
    @Max(365)
    expiring_in_days?: number;
}
