import { z } from 'zod';
import { ApiResponseSchema } from '#contracts/shared';
import { normalizePhone } from '#utils/stringUtils';

// ---------------------------------------------------------------------------
// Request schemas
// ---------------------------------------------------------------------------

export const LoginBodySchema = z
	.object({
		email: z.email().openapi({ example: 'user@example.com' }),
		password: z.string().min(1).max(128).openapi({ example: 'SecurePass123' }),
		device_info: z
			.record(z.string(), z.unknown())
			.optional()
			.openapi({
				example: {
					device_type: 'mobile',
					os: 'iOS 17.0',
					browser: 'Safari',
					model: 'iPhone 15 Pro',
				},
			}),
	})
	.openapi('LoginBody');

export const MobileLoginBodySchema = z
	.object({
		phone: z
			.string()
			.transform((val) => {
				const normalized = normalizePhone(val);
				if (!normalized) {
					throw new Error('Invalid phone number format');
				}
				return normalized;
			})
			.openapi({ example: '0901234567' }),
		password: z.string().min(1).max(128).openapi({ example: 'SecurePass123' }),
		device_info: z
			.record(z.string(), z.unknown())
			.optional()
			.openapi({
				example: {
					device_type: 'mobile',
					os: 'iOS 17.0',
					browser: 'Safari',
					model: 'iPhone 15 Pro',
				},
			}),
	})
	.openapi('MobileLoginBody');

export const WebLoginBodySchema = z
	.object({
		email: z.email().openapi({ example: 'user@example.com' }),
		password: z.string().min(1).max(128).openapi({ example: 'SecurePass123' }),
		device_info: z
			.record(z.string(), z.unknown())
			.optional()
			.openapi({
				example: {
					device_type: 'desktop',
					os: 'Windows 11',
					browser: 'Chrome 120',
					resolution: '1920x1080',
				},
			}),
	})
	.openapi('WebLoginBody');

export const RegisterBodySchema = z
	.object({
		email: z.email().openapi({ example: 'user@example.com' }),
		password: z.string().min(8).max(128).openapi({ example: 'SecurePass123' }),
		username: z.string().optional().openapi({ example: 'johndoe' }),
		first_name: z.string().optional().openapi({ example: 'John' }),
		last_name: z.string().optional().openapi({ example: 'Doe' }),
		phone: z
			.string()
			.regex(/^(0\d{9}|\+84\d{9}|\+?[1-9]\d{9,14})$/, 'Invalid phone number format')
			.optional()
			.openapi({ example: '0333748720' }),
	})
	.openapi('RegisterBody');

export const RefreshTokenBodySchema = z
	.object({
		refresh_token: z.string().optional().openapi({ example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' }),
		device_info: z
			.record(z.string(), z.unknown())
			.optional()
			.openapi({
				example: {
					device_type: 'mobile',
					os: 'iOS 17.0',
					browser: 'Safari',
				},
			}),
	})
	.openapi('RefreshTokenBody');

export const ChangePasswordBodySchema = z
	.object({
		old_password: z.string().min(1).max(128).openapi({ example: 'OldSecurePass123' }),
		new_password: z.string().min(8).max(128).openapi({ example: 'NewSecurePass456' }),
	})
	.openapi('ChangePasswordBody');

export const ChangePasswordResponseDataSchema = z
	.object({
		success: z.boolean(),
		message: z.string(),
	})
	.openapi('ChangePasswordResponseData');

export const ChangePasswordResponseSchema = ApiResponseSchema(ChangePasswordResponseDataSchema).openapi(
	'ChangePasswordResponse',
);
export type ChangePasswordResponse = z.infer<typeof ChangePasswordResponseSchema>;

// ---------------------------------------------------------------------------
// Response data schemas (the shape inside `responseData`)
// ---------------------------------------------------------------------------

/** Public user fields returned to the client — never includes password_hash, deleted_at, etc. */
export const UserPublicSchema = z
	.object({
		id: z.uuid(),
		email: z.email(),
		username: z.string().nullable(),
		first_name: z.string().nullable(),
		last_name: z.string().nullable(),
		status: z.string(),
		avatar_id: z.uuid().nullable().optional(),
		roles: z.array(z.string()),
		permissions: z.array(z.string()),
		last_login_at: z.string().nullable().optional(),
		avatar_url: z.string().nullable().optional(),
	})
	.openapi('UserPublic');

export type UserPublic = z.infer<typeof UserPublicSchema>;

export const MobileLoginDataSchema = z
	.object({
		user: UserPublicSchema,
		access_token: z.string(),
		refresh_token: z.string(),
		token_type: z.literal('Bearer'),
	})
	.openapi('MobileLoginData');

export type MobileLoginData = z.infer<typeof MobileLoginDataSchema>;

export const RegisterResponseDataSchema = z
	.object({
		id: z.uuid(),
		email: z.email(),
		username: z.string().nullable(),
		status: z.string(),
	})
	.openapi('RegisterResponseData');

export type RegisterResponseData = z.infer<typeof RegisterResponseDataSchema>;

export const RefreshTokenDataSchema = z
	.object({
		session: z.object({
			expires_at: z.iso.datetime(),
			refresh_expires_at: z.iso.datetime(),
		}),
		// tokens only present in development mode
		access_token: z.string().optional(),
		refresh_token: z.string().optional(),
		token_type: z.literal('Bearer').optional(),
	})
	.openapi('RefreshTokenData');

// ---------------------------------------------------------------------------
// Full response schemas (envelope + data) — used by controllers with `satisfies`
// ---------------------------------------------------------------------------

export const LoginMobileResponseSchema = ApiResponseSchema(MobileLoginDataSchema).openapi('LoginMobileResponse');
export type LoginMobileResponse = z.infer<typeof LoginMobileResponseSchema>;

export const RegisterResponseSchema = ApiResponseSchema(RegisterResponseDataSchema).openapi('RegisterResponse');
export type RegisterResponse = z.infer<typeof RegisterResponseSchema>;

export const RefreshTokenResponseSchema = ApiResponseSchema(RefreshTokenDataSchema).openapi('RefreshTokenResponse');
export type RefreshTokenResponse = z.infer<typeof RefreshTokenResponseSchema>;

export const ProfileResponseSchema = ApiResponseSchema(UserPublicSchema).openapi('ProfileResponse');
export type ProfileResponse = z.infer<typeof ProfileResponseSchema>;

// ---------------------------------------------------------------------------
// Zalo login schemas
// ---------------------------------------------------------------------------

export const ZaloLoginBodySchema = z
	.object({
		zalo_code: z.string().optional().openapi({ example: 'zalo_auth_code_12345' }),
		zalo_open_id: z.string().optional().openapi({ example: '123456789' }),
		device_info: z
			.record(z.string(), z.unknown())
			.optional()
			.openapi({
				example: {
					device_type: 'zalo_mini_app',
					os: 'iOS',
					app_version: '1.0.0',
				},
			}),
	})
	.openapi('ZaloLoginBody');

export const ZaloLoginResponseSchema = ApiResponseSchema(MobileLoginDataSchema).openapi('ZaloLoginResponse');
export type ZaloLoginResponse = z.infer<typeof ZaloLoginResponseSchema>;

// ---------------------------------------------------------------------------
// OTP schemas
// ---------------------------------------------------------------------------

export const SendConfirmOtpResponseDataSchema = z
	.object({
		expiresInSeconds: z.number(),
		otp: z.string().optional(), // Only in development
	})
	.openapi('SendConfirmOtpResponseData');

export const SendConfirmOtpResponseSchema = ApiResponseSchema(SendConfirmOtpResponseDataSchema).openapi(
	'SendConfirmOtpResponse',
);
export type SendConfirmOtpResponse = z.infer<typeof SendConfirmOtpResponseSchema>;

// ---------------------------------------------------------------------------
// Session schemas
// ---------------------------------------------------------------------------

export const RevokeSessionResponseSchema = ApiResponseSchema(z.null()).openapi('RevokeSessionResponse');
export type RevokeSessionResponse = z.infer<typeof RevokeSessionResponseSchema>;

// ---------------------------------------------------------------------------
// Web login schemas
// ---------------------------------------------------------------------------

export const WebLoginDataSchema = z
	.object({
		user: UserPublicSchema,
		session: z.object({
			id: z.uuid(),
			expires_at: z.iso.datetime(),
			refresh_expires_at: z.iso.datetime(),
			expires_in: z.number(),
			refresh_expires_in: z.number(),
			token_type: z.literal('Bearer'),
		}),
		access_token: z.string().optional(),
		refresh_token: z.string().optional(),
	})
	.openapi('WebLoginData');

export const WebLoginResponseSchema = ApiResponseSchema(WebLoginDataSchema).openapi('WebLoginResponse');
export type WebLoginResponse = z.infer<typeof WebLoginResponseSchema>;

// ---------------------------------------------------------------------------
// Session list schema
// ---------------------------------------------------------------------------

export const SessionListResponseSchema = ApiResponseSchema(
	z.object({
		count: z.number(),
		rows: z.array(
			z.object({
				id: z.uuid(),
				platform: z.string(),
				device_id: z.string().nullable(),
				device_name: z.string().nullable(),
				ip_address: z.string().nullable(),
				user_agent: z.string().nullable(),
				last_activity_at: z.iso.datetime(),
				created_at: z.iso.datetime(),
				expires_at: z.iso.datetime(),
			}),
		),
		page: z.number(),
		pageSize: z.number(),
	}),
).openapi('SessionListResponse');
export type SessionListResponse = z.infer<typeof SessionListResponseSchema>;

// ---------------------------------------------------------------------------
// Forgot password schemas
// ---------------------------------------------------------------------------

export const PasswordResetRequestSchema = z
	.object({
		email: z.email(),
	})
	.openapi('PasswordResetRequest');

export const PasswordResetSubmitSchema = z
	.object({
		email: z.email(),
		code: z.string().length(6),
		new_password: z.string().min(8).max(128),
	})
	.openapi('PasswordResetSubmit');

export const PasswordResetSubmitResponseSchema = ApiResponseSchema(
	z.object({
		success: z.boolean(),
	}),
).openapi('PasswordResetSubmitResponse');
export type PasswordResetSubmitResponse = z.infer<typeof PasswordResetSubmitResponseSchema>;

export const PasswordResetRequestResponseDataSchema = z
	.object({
		sent: z.boolean(),
		expiresIn: z.number(),
	})
	.openapi('PasswordResetRequestResponseData');

export const PasswordResetRequestResponseSchema = ApiResponseSchema(PasswordResetRequestResponseDataSchema).openapi(
	'PasswordResetRequestResponse',
);
export type PasswordResetRequestResponse = z.infer<typeof PasswordResetRequestResponseSchema>;

// ---------------------------------------------------------------------------
// Email verification schemas
// ---------------------------------------------------------------------------

export const ResendVerificationBodySchema = z
	.object({
		email: z.email().openapi({ example: 'user@example.com' }),
	})
	.openapi('ResendVerificationBody');

export const ResendVerificationResponseDataSchema = z
	.object({
		sent: z.boolean(),
		expires_at: z.iso.datetime().nullable(),
		email_mode: z.string(),
		message: z.string(),
	})
	.openapi('ResendVerificationResponseData');

export type ResendVerificationResponseData = z.infer<typeof ResendVerificationResponseDataSchema>;

export const VerifyEmailQuerySchema = z
	.object({
		token: z.string().min(1).openapi({ example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' }),
	})
	.openapi('VerifyEmailQuery');

export const VerifyEmailResponseDataSchema = z
	.object({
		verified: z.boolean(),
		email: z.email(),
		status: z.string(),
		email_verified_at: z.iso.datetime(),
		message: z.string(),
		auto_login: z.boolean().optional(),
		auto_login_error: z.string().optional(),
		redirect_to: z.string().optional(),
	})
	.openapi('VerifyEmailResponseData');

export type VerifyEmailResponseData = z.infer<typeof VerifyEmailResponseDataSchema>;
