from typing import Optional, TypedDict, Union, Dict, List
from pydantic import BaseModel, root_validator, Field
from datetime import datetime, timedelta
from fastapi import Query

class IndexAPI(BaseModel):
    symbol: str

class GraphDataInput(BaseModel):
    symbol: str
    exchange: str
    offset: Optional[str] = "3"
    duration: Optional[str] = "5Y"

class ModelInput(BaseModel):
    symbol: str
    exchange: str
    time_range: Optional[str] = '1W'
    start_date: Optional[str] = None
    end_date: Optional[str] = None

class AnalyzeMultipleRequest(BaseModel):
    instrument: str
    duration: Optional[str] = '1Y'  # Default value is '1Y'
    exchange: Optional[str] = 'NSE'
    length: int = Query(100, ge=1, description="Number of items per page")
    offset: int = Query(0, ge=0, description="Offset to start fetching data")

class PortfolioReportGet(BaseModel):
    investor: str
    data_type: int
    start_date: Optional[str] = None
    end_date: Optional[str] = None

    @root_validator(pre=True)
    def set_default_dates(cls, values):
        if values.get("start_date") is None and values.get("end_date") is None:
            end_date = datetime.today()
            start_date = end_date - timedelta(days=30)
            values["start_date"] = start_date.strftime("%Y-%m-%d")
            values["end_date"] = end_date.strftime("%Y-%m-%d")
        return values

class PortfolioCreate(BaseModel):
    investor: str
    description: Optional[str] = None

# Pydantic model to define the structure of each report
class PortfolioFilterParams(BaseModel):
    investor: Optional[str] = None

class PortfolioHolding(BaseModel):
    symbol: str
    qty: int
    avg_price: float

class ReportList(BaseModel):
    symbol: str
    avg_price: float
    qty: int

class PortfolioReportList(BaseModel):
    investor: str
    holdings: List[PortfolioHolding]

class ReportType(BaseModel):
    reqType: int
    start_date: Optional[str] = None
    end_date: Optional[str] = None

    @root_validator(pre=True)
    def set_default_dates(cls, values):
        if values.get("start_date") is None and values.get("end_date") is None:
            end_date = datetime.today()
            start_date = end_date - timedelta(days=30)
            values["start_date"] = start_date.strftime("%Y-%m-%d")
            values["end_date"] = end_date.strftime("%Y-%m-%d")
        return values

class PriceData(TypedDict):
    date: datetime
    price_multiplied: float

class SymbolReport(TypedDict):
    type: str
    symbol: str
    qty: Union[float, str]
    avg_price: Union[float, str]

# Pydantic model to define the structure of each order
class Order(BaseModel):
    symbol: str
    amount: float
    quantity: int
    exchange: str
    action: str
    instrument_token: Optional[int] = None
    exchange_token: Optional[int] = None

class DeleteOrd(BaseModel):
    order_id: list[Dict[str, str]]

# User model for registration
class User(BaseModel):
    username: str
    email: str
    password: str

# User model for internal DB handling (without password)
class UserInDB(User):
    hashed_password: str

class FilterItem(BaseModel):
    filter: str
    condition: str
    value: Union[str, List[str]] = Field(..., description="The value to evaluate against the filter. Can be a single string or a list of strings.")
    operator: Optional[str] = None

class ReportDetails_NSE(BaseModel):
    input_symbol: Optional[str] = None
    duration: Optional[str] = '1Y'
    Instrument: Optional[str] = 'Equity'
    start_date: Optional[str] = None
    end_date: Optional[str] = None
    type: Optional[int] = 0
    filters: Optional[List[FilterItem]] = None

class ReportDetails_BSE(BaseModel):
    input_symbol: Optional[str] = None
    duration: Optional[str] = '1Y'
    Instrument: Optional[str] = 'Equity'
    start_date: Optional[str] = None
    end_date: Optional[str] = None
    type: Optional[int] = 0
    filters: Optional[List[FilterItem]] = None

class ConditionItem_test(BaseModel):
    filter: str
    condition: str
    value: Union[str, List[str]] = Field(
        ..., description="The value to evaluate against the filter. Can be a single string or a list of strings."
    )
    operator: Optional[str] = None


class FilterItem_test(BaseModel):
    timeRange: List[str] = Field(
        ..., description="The start and end dates for the time range in the format [start_date, end_date]."
    )
    conditions: List[ConditionItem_test] = Field(
        ..., description="A list of conditions to apply within this filter."
    )

class ReportDetails_test(BaseModel):
    input_symbol: Optional[str] = None
    duration: Optional[str] = "1Y"
    Instrument: Optional[str] = "Equity"
    start_date: Optional[str] = None
    end_date: Optional[str] = None
    type: Optional[int] = 0
    filters: Optional[List[FilterItem_test]] = None

class PortfolioDelete(BaseModel):
    investorName: str

"""Models for Query Storing"""
class Condition(BaseModel):
    filter: str
    condition: str
    value: str
    operator: str

class TimeRangeQuery(BaseModel):
    timeRange: List[str]
    conditions: List[Condition]

class QueryStore(BaseModel):
    queryName: str
    report_type: str
    description: Optional[str] = ""  # Added description at the root level
    payload: List[TimeRangeQuery]

"""Models for Sections API"""
class SecCondition(BaseModel):
    filter: str
    condition: str
    value: str
    operator: str

class SecPayload(BaseModel):
    timeRange: List[str]
    conditions: List[SecCondition]

class SecQuery(BaseModel):
    queryName: str
    description: str
    payload: List[SecPayload]
    report_type: str

class Section(BaseModel):
    sectionName: str
    description: str
    # Queries: List[SecQuery]
    Queries: str
    queryType: str
    report_type: str
