from pydantic import BaseModel, Field, field_validator, ConfigDict
from typing import Optional, List
from datetime import datetime, date
from decimal import Decimal
from enum import Enum


class StockStatus(str, Enum):
    ACTIVE = "ACTIVE"
    INACTIVE = "INACTIVE"


# Sector schemas
class SectorBase(BaseModel):
    name: str = Field(..., min_length=1, max_length=255)
    description: Optional[str] = None
    icon_url: Optional[str] = Field(None, max_length=500)
    display_order: int = Field(default=0)

    @field_validator("name")
    @classmethod
    def validate_name(cls, v: str) -> str:
        if not v or not v.strip():
            raise ValueError("Sector name cannot be empty or whitespace")
        if len(v.strip()) > 255:
            raise ValueError("Sector name must not exceed 255 characters")
        return v.strip()

    @field_validator("icon_url")
    @classmethod
    def validate_icon_url(cls, v: Optional[str]) -> Optional[str]:
        if v and v.strip():
            if not v.startswith(("http://", "https://")):
                raise ValueError("Icon URL must be a valid HTTP or HTTPS URL")
            if len(v) > 500:
                raise ValueError("Icon URL must not exceed 500 characters")
            return v.strip()
        return None

    @field_validator("display_order")
    @classmethod
    def validate_display_order(cls, v: int) -> int:
        if v < 0:
            raise ValueError("Display order must be non-negative")
        return v


class SectorCreate(SectorBase):
    pass


class SectorUpdate(BaseModel):
    name: Optional[str] = Field(None, min_length=1, max_length=255)
    description: Optional[str] = None
    icon_url: Optional[str] = Field(None, max_length=500)
    display_order: Optional[int] = None

    @field_validator("name")
    @classmethod
    def validate_name(cls, v: Optional[str]) -> Optional[str]:
        if v is not None:
            if not v.strip():
                raise ValueError("Sector name cannot be empty or whitespace")
            if len(v.strip()) > 255:
                raise ValueError("Sector name must not exceed 255 characters")
            return v.strip()
        return None

    @field_validator("icon_url")
    @classmethod
    def validate_icon_url(cls, v: Optional[str]) -> Optional[str]:
        if v is not None and v.strip():
            if not v.startswith(("http://", "https://")):
                raise ValueError("Icon URL must be a valid HTTP or HTTPS URL")
            if len(v) > 500:
                raise ValueError("Icon URL must not exceed 500 characters")
            return v.strip()
        return v

    @field_validator("display_order")
    @classmethod
    def validate_display_order(cls, v: Optional[int]) -> Optional[int]:
        if v is not None and v < 0:
            raise ValueError("Display order must be non-negative")
        return v


class SectorResponse(SectorBase):
    id: str
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)


# Exchange schemas
class ExchangeBase(BaseModel):
    name: str = Field(..., min_length=1, max_length=255)
    code: str = Field(..., min_length=1, max_length=50)
    country: str = Field(..., min_length=1, max_length=100)
    timezone: str = Field(..., min_length=1, max_length=100)
    trading_hours: Optional[str] = Field(None, max_length=255)
    currency: str = Field(..., min_length=3, max_length=10)
    website_url: Optional[str] = Field(None, max_length=500)
    description: Optional[str] = None

    @field_validator("name")
    @classmethod
    def validate_name(cls, v: str) -> str:
        if not v or not v.strip():
            raise ValueError("Exchange name cannot be empty or whitespace")
        if len(v.strip()) > 255:
            raise ValueError("Exchange name must not exceed 255 characters")
        return v.strip()

    @field_validator("code")
    @classmethod
    def validate_code(cls, v: str) -> str:
        if not v or not v.strip():
            raise ValueError("Exchange code cannot be empty or whitespace")
        code = v.strip().upper()
        if len(code) > 50:
            raise ValueError("Exchange code must not exceed 50 characters")
        return code

    @field_validator("country")
    @classmethod
    def validate_country(cls, v: str) -> str:
        if not v or not v.strip():
            raise ValueError("Country cannot be empty or whitespace")
        if len(v.strip()) > 100:
            raise ValueError("Country must not exceed 100 characters")
        return v.strip()

    @field_validator("timezone")
    @classmethod
    def validate_timezone(cls, v: str) -> str:
        if not v or not v.strip():
            raise ValueError("Timezone cannot be empty or whitespace")
        if len(v.strip()) > 100:
            raise ValueError("Timezone must not exceed 100 characters")
        return v.strip()

    @field_validator("currency")
    @classmethod
    def validate_currency(cls, v: str) -> str:
        if not v or not v.strip():
            raise ValueError("Currency cannot be empty or whitespace")
        currency = v.strip().upper()
        if len(currency) < 3 or len(currency) > 10:
            raise ValueError("Currency must be 3-10 characters (ISO 4217 code)")
        return currency

    @field_validator("website_url")
    @classmethod
    def validate_website_url(cls, v: Optional[str]) -> Optional[str]:
        if v and v.strip():
            if not v.startswith(("http://", "https://")):
                raise ValueError("Website URL must be a valid HTTP or HTTPS URL")
            if len(v) > 500:
                raise ValueError("Website URL must not exceed 500 characters")
            return v.strip()
        return None


class ExchangeCreate(ExchangeBase):
    pass


class ExchangeUpdate(BaseModel):
    name: Optional[str] = Field(None, min_length=1, max_length=255)
    code: Optional[str] = Field(None, min_length=1, max_length=50)
    country: Optional[str] = Field(None, min_length=1, max_length=100)
    timezone: Optional[str] = Field(None, min_length=1, max_length=100)
    trading_hours: Optional[str] = Field(None, max_length=255)
    currency: Optional[str] = Field(None, min_length=3, max_length=10)
    website_url: Optional[str] = Field(None, max_length=500)
    description: Optional[str] = None

    @field_validator("name")
    @classmethod
    def validate_name(cls, v: Optional[str]) -> Optional[str]:
        if v is not None:
            if not v.strip():
                raise ValueError("Exchange name cannot be empty or whitespace")
            if len(v.strip()) > 255:
                raise ValueError("Exchange name must not exceed 255 characters")
            return v.strip()
        return None

    @field_validator("code")
    @classmethod
    def validate_code(cls, v: Optional[str]) -> Optional[str]:
        if v is not None:
            if not v.strip():
                raise ValueError("Exchange code cannot be empty or whitespace")
            code = v.strip().upper()
            if len(code) > 50:
                raise ValueError("Exchange code must not exceed 50 characters")
            return code
        return None

    @field_validator("country")
    @classmethod
    def validate_country(cls, v: Optional[str]) -> Optional[str]:
        if v is not None:
            if not v.strip():
                raise ValueError("Country cannot be empty or whitespace")
            if len(v.strip()) > 100:
                raise ValueError("Country must not exceed 100 characters")
            return v.strip()
        return None

    @field_validator("timezone")
    @classmethod
    def validate_timezone(cls, v: Optional[str]) -> Optional[str]:
        if v is not None:
            if not v.strip():
                raise ValueError("Timezone cannot be empty or whitespace")
            if len(v.strip()) > 100:
                raise ValueError("Timezone must not exceed 100 characters")
            return v.strip()
        return None

    @field_validator("currency")
    @classmethod
    def validate_currency(cls, v: Optional[str]) -> Optional[str]:
        if v is not None:
            if not v.strip():
                raise ValueError("Currency cannot be empty or whitespace")
            currency = v.strip().upper()
            if len(currency) < 3 or len(currency) > 10:
                raise ValueError("Currency must be 3-10 characters (ISO 4217 code)")
            return currency
        return None

    @field_validator("website_url")
    @classmethod
    def validate_website_url(cls, v: Optional[str]) -> Optional[str]:
        if v is not None and v.strip():
            if not v.startswith(("http://", "https://")):
                raise ValueError("Website URL must be a valid HTTP or HTTPS URL")
            if len(v) > 500:
                raise ValueError("Website URL must not exceed 500 characters")
            return v.strip()
        return v


class ExchangeResponse(ExchangeBase):
    id: str
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)


# Stock schemas
class StockBase(BaseModel):
    ticker_symbol: str = Field(..., min_length=1, max_length=50)
    company_name: str = Field(..., min_length=1, max_length=255)
    current_price: Optional[Decimal] = None
    opening_price: Optional[Decimal] = None
    closing_price: Optional[Decimal] = None
    high_price: Optional[Decimal] = None
    low_price: Optional[Decimal] = None
    volume: Optional[int] = None
    market_cap: Optional[Decimal] = None
    sector_id: str
    industry: Optional[str] = Field(None, max_length=255)
    exchange_id: str
    currency: str = Field(..., min_length=3, max_length=10)
    last_updated: Optional[datetime] = None
    description: Optional[str] = None
    logo_url: Optional[str] = Field(None, max_length=500)
    website_url: Optional[str] = Field(None, max_length=500)
    country: Optional[str] = Field(None, max_length=100)
    ipo_date: Optional[date] = None
    status: StockStatus = Field(default=StockStatus.ACTIVE)

    @field_validator("ticker_symbol")
    @classmethod
    def validate_ticker_symbol(cls, v: str) -> str:
        if not v or not v.strip():
            raise ValueError("Ticker symbol cannot be empty or whitespace")
        ticker = v.strip().upper()
        if len(ticker) < 1 or len(ticker) > 10:
            raise ValueError("Ticker symbol must be 1-10 uppercase characters")
        if not ticker.isalpha():
            raise ValueError("Ticker symbol must contain only uppercase letters")
        return ticker

    @field_validator("company_name")
    @classmethod
    def validate_company_name(cls, v: str) -> str:
        if not v or not v.strip():
            raise ValueError("Company name cannot be empty or whitespace")
        if len(v.strip()) > 255:
            raise ValueError("Company name must not exceed 255 characters")
        return v.strip()

    @field_validator("current_price", "opening_price", "closing_price", "high_price", "low_price")
    @classmethod
    def validate_price(cls, v: Optional[Decimal]) -> Optional[Decimal]:
        if v is not None:
            if v <= 0:
                raise ValueError("Price must be positive")
            if v.as_tuple().exponent < -4:
                raise ValueError("Price must have at most 4 decimal places")
        return v

    @field_validator("volume")
    @classmethod
    def validate_volume(cls, v: Optional[int]) -> Optional[int]:
        if v is not None and v < 0:
            raise ValueError("Volume must be non-negative")
        return v

    @field_validator("market_cap")
    @classmethod
    def validate_market_cap(cls, v: Optional[Decimal]) -> Optional[Decimal]:
        if v is not None and v <= 0:
            raise ValueError("Market cap must be positive")
        return v

    @field_validator("currency")
    @classmethod
    def validate_currency(cls, v: str) -> str:
        if not v or not v.strip():
            raise ValueError("Currency cannot be empty or whitespace")
        currency = v.strip().upper()
        if len(currency) < 3 or len(currency) > 10:
            raise ValueError("Currency must be 3-10 characters (ISO 4217 code)")
        return currency

    @field_validator("logo_url", "website_url")
    @classmethod
    def validate_url(cls, v: Optional[str]) -> Optional[str]:
        if v and v.strip():
            if not v.startswith(("http://", "https://")):
                raise ValueError("URL must be a valid HTTP or HTTPS URL")
            if len(v) > 500:
                raise ValueError("URL must not exceed 500 characters")
            return v.strip()
        return None

    @field_validator("ipo_date")
    @classmethod
    def validate_ipo_date(cls, v: Optional[date]) -> Optional[date]:
        if v is not None and v > date.today():
            raise ValueError("IPO date cannot be in the future")
        return v


class StockCreate(StockBase):
    pass


class StockUpdate(BaseModel):
    ticker_symbol: Optional[str] = Field(None, min_length=1, max_length=50)
    company_name: Optional[str] = Field(None, min_length=1, max_length=255)
    current_price: Optional[Decimal] = None
    opening_price: Optional[Decimal] = None
    closing_price: Optional[Decimal] = None
    high_price: Optional[Decimal] = None
    low_price: Optional[Decimal] = None
    volume: Optional[int] = None
    market_cap: Optional[Decimal] = None
    sector_id: Optional[str] = None
    industry: Optional[str] = Field(None, max_length=255)
    exchange_id: Optional[str] = None
    currency: Optional[str] = Field(None, min_length=3, max_length=10)
    last_updated: Optional[datetime] = None
    description: Optional[str] = None
    logo_url: Optional[str] = Field(None, max_length=500)
    website_url: Optional[str] = Field(None, max_length=500)
    country: Optional[str] = Field(None, max_length=100)
    ipo_date: Optional[date] = None
    status: Optional[StockStatus] = None

    @field_validator("ticker_symbol")
    @classmethod
    def validate_ticker_symbol(cls, v: Optional[str]) -> Optional[str]:
        if v is not None:
            if not v.strip():
                raise ValueError("Ticker symbol cannot be empty or whitespace")
            ticker = v.strip().upper()
            if len(ticker) < 1 or len(ticker) > 10:
                raise ValueError("Ticker symbol must be 1-10 uppercase characters")
            if not ticker.isalpha():
                raise ValueError("Ticker symbol must contain only uppercase letters")
            return ticker
        return None

    @field_validator("company_name")
    @classmethod
    def validate_company_name(cls, v: Optional[str]) -> Optional[str]:
        if v is not None:
            if not v.strip():
                raise ValueError("Company name cannot be empty or whitespace")
            if len(v.strip()) > 255:
                raise ValueError("Company name must not exceed 255 characters")
            return v.strip()
        return None

    @field_validator("current_price", "opening_price", "closing_price", "high_price", "low_price")
    @classmethod
    def validate_price(cls, v: Optional[Decimal]) -> Optional[Decimal]:
        if v is not None:
            if v <= 0:
                raise ValueError("Price must be positive")
            if v.as_tuple().exponent < -4:
                raise ValueError("Price must have at most 4 decimal places")
        return v

    @field_validator("volume")
    @classmethod
    def validate_volume(cls, v: Optional[int]) -> Optional[int]:
        if v is not None and v < 0:
            raise ValueError("Volume must be non-negative")
        return v

    @field_validator("market_cap")
    @classmethod
    def validate_market_cap(cls, v: Optional[Decimal]) -> Optional[Decimal]:
        if v is not None and v <= 0:
            raise ValueError("Market cap must be positive")
        return v

    @field_validator("currency")
    @classmethod
    def validate_currency(cls, v: Optional[str]) -> Optional[str]:
        if v is not None:
            if not v.strip():
                raise ValueError("Currency cannot be empty or whitespace")
            currency = v.strip().upper()
            if len(currency) < 3 or len(currency) > 10:
                raise ValueError("Currency must be 3-10 characters (ISO 4217 code)")
            return currency
        return None

    @field_validator("logo_url", "website_url")
    @classmethod
    def validate_url(cls, v: Optional[str]) -> Optional[str]:
        if v is not None and v.strip():
            if not v.startswith(("http://", "https://")):
                raise ValueError("URL must be a valid HTTP or HTTPS URL")
            if len(v) > 500:
                raise ValueError("URL must not exceed 500 characters")
            return v.strip()
        return v

    @field_validator("ipo_date")
    @classmethod
    def validate_ipo_date(cls, v: Optional[date]) -> Optional[date]:
        if v is not None and v > date.today():
            raise ValueError("IPO date cannot be in the future")
        return v


class StockResponse(StockBase):
    id: str
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)


class StockDetailResponse(BaseModel):
    id: str
    ticker_symbol: str
    company_name: str
    current_price: Optional[Decimal]
    opening_price: Optional[Decimal]
    closing_price: Optional[Decimal]
    high_price: Optional[Decimal]
    low_price: Optional[Decimal]
    volume: Optional[int]
    market_cap: Optional[Decimal]
    industry: Optional[str]
    currency: str
    last_updated: Optional[datetime]
    description: Optional[str]
    logo_url: Optional[str]
    website_url: Optional[str]
    country: Optional[str]
    ipo_date: Optional[date]
    status: str
    created_at: datetime
    updated_at: datetime
    sector: "SectorResponse"
    exchange: "ExchangeResponse"

    model_config = ConfigDict(from_attributes=True)


# StockHistory schemas
class StockhistoryBase(BaseModel):
    stock_id: str
    date: date
    open_price: Decimal
    close_price: Decimal
    high_price: Decimal
    low_price: Decimal
    volume: int
    adjusted_close_price: Optional[Decimal] = None

    @field_validator("open_price", "close_price", "high_price", "low_price")
    @classmethod
    def validate_price(cls, v: Decimal) -> Decimal:
        if v <= 0:
            raise ValueError("Price must be positive")
        if v.as_tuple().exponent < -4:
            raise ValueError("Price must have at most 4 decimal places")
        return v

    @field_validator("adjusted_close_price")
    @classmethod
    def validate_adjusted_close_price(cls, v: Optional[Decimal]) -> Optional[Decimal]:
        if v is not None:
            if v <= 0:
                raise ValueError("Adjusted close price must be positive")
            if v.as_tuple().exponent < -4:
                raise ValueError("Adjusted close price must have at most 4 decimal places")
        return v

    @field_validator("volume")
    @classmethod
    def validate_volume(cls, v: int) -> int:
        if v < 0:
            raise ValueError("Volume must be non-negative")
        return v


class StockhistoryCreate(StockhistoryBase):
    pass


class StockhistoryUpdate(BaseModel):
    stock_id: Optional[str] = None
    date: Optional[date] = None
    open_price: Optional[Decimal] = None
    close_price: Optional[Decimal] = None
    high_price: Optional[Decimal] = None
    low_price: Optional[Decimal] = None
    volume: Optional[int] = None
    adjusted_close_price: Optional[Decimal] = None

    @field_validator("open_price", "close_price", "high_price", "low_price")
    @classmethod
    def validate_price(cls, v: Optional[Decimal]) -> Optional[Decimal]:
        if v is not None:
            if v <= 0:
                raise ValueError("Price must be positive")
            if v.as_tuple().exponent < -4:
                raise ValueError("Price must have at most 4 decimal places")
        return v

    @field_validator("adjusted_close_price")
    @classmethod
    def validate_adjusted_close_price(cls, v: Optional[Decimal]) -> Optional[Decimal]:
        if v is not None:
            if v <= 0:
                raise ValueError("Adjusted close price must be positive")
            if v.as_tuple().exponent < -4:
                raise ValueError("Adjusted close price must have at most 4 decimal places")
        return v

    @field_validator("volume")
    @classmethod
    def validate_volume(cls, v: Optional[int]) -> Optional[int]:
        if v is not None and v < 0:
            raise ValueError("Volume must be non-negative")
        return v


class StockhistoryResponse(StockhistoryBase):
    id: str
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)


class StockWithHistoryResponse(BaseModel):
    id: str
    ticker_symbol: str
    company_name: str
    current_price: Optional[Decimal]
    status: str
    stock_history: List[StockhistoryResponse]

    model_config = ConfigDict(from_attributes=True)


class BulkImportStockItem(BaseModel):
    ticker_symbol: str
    company_name: str
    sector_name: str
    exchange_code: str
    currency: str
    current_price: Optional[Decimal] = None
    industry: Optional[str] = None
    description: Optional[str] = None
    country: Optional[str] = None
    ipo_date: Optional[date] = None


class BulkImportStocksRequest(BaseModel):
    stocks: List[BulkImportStockItem]

    @field_validator("stocks")
    @classmethod
    def validate_stocks(cls, v: List[BulkImportStockItem]) -> List[BulkImportStockItem]:
        if not v:
            raise ValueError("Stocks list cannot be empty")
        if len(v) > 10000:
            raise ValueError("Bulk import cannot exceed 10,000 rows")
        return v


class BulkImportStocksResponse(BaseModel):
    total: int
    successful: int
    failed: int
    errors: List[str]


class BulkImportHistoryItem(BaseModel):
    ticker_symbol: str
    date: date
    open_price: Decimal
    close_price: Decimal
    high_price: Decimal
    low_price: Decimal
    volume: int
    adjusted_close_price: Optional[Decimal] = None


class BulkImportHistoryRequest(BaseModel):
    history_items: List[BulkImportHistoryItem]

    @field_validator("history_items")
    @classmethod
    def validate_history_items(cls, v: List[BulkImportHistoryItem]) -> List[BulkImportHistoryItem]:
        if not v:
            raise ValueError("History items list cannot be empty")
        if len(v) > 10000:
            raise ValueError("Bulk import cannot exceed 10,000 rows")
        return v


class BulkImportHistoryResponse(BaseModel):
    total: int
    successful: int
    failed: int
    errors: List[str]