#pragma once
#include "trading/BrokerInterface.h"
#include "trading/adapter/BrokerEnumMap.h"
#include "trading/brokers/BrokerHttp.h"

#include <functional>

namespace fincept::trading {

class ZerodhaBroker : public IBroker {
  public:
    BrokerId id() const override { return BrokerId::Zerodha; }
    const char* name() const override { return "Zerodha"; }
    const char* base_url() const override { return "https://api.kite.trade"; }
    const char* ws_adapter_name() const override { return "zerodha"; }

    BrokerProfile profile() const override {
        return BrokerProfile{
            .id = "zerodha",
            .display_name = "Zerodha",
            .region = "IN",
            .currency = "INR",
            .credential_fields =
                {
                    {CredentialField::ApiKey, "API KEY", "Enter API Key...", false},
                    {CredentialField::ApiSecret, "API SECRET", "Enter API Secret...", true},
                    {CredentialField::AuthCode, "REQUEST TOKEN", "Paste request_token...", false},
                },
            .exchanges = {"NSE", "BSE", "NFO", "CDS"},
            .product_types =
                {
                    {"Intraday (MIS)", ProductType::Intraday},
                    {"Delivery (CNC)", ProductType::Delivery},
                    {"Margin (NRML)", ProductType::Margin},
                    {"MTF (Margin Trading Facility)", ProductType::MTF},
                    {"Cover Order", ProductType::CoverOrder},
                    {"Bracket Order", ProductType::BracketOrder},
                },
            .supports_intraday = true,
            .supports_bracket_order = true,
            .supports_cover_order = true,
            .has_native_paper = false,
            .default_paper_balance = 1000000.0,
            .default_watchlist = {"HDFCBANK", "ICICIBANK", "SBIN", "KOTAKBANK", "AXISBANK", "TCS", "INFY", "RELIANCE",
                                  "TATAMOTORS", "BAJFINANCE"},
            .default_symbol = "RELIANCE",
            .default_exchange = "NSE",
            .brokerage_info = "\u20B920/order or 0.03% intraday, 0% delivery",
        };
    }

    TokenExchangeResponse exchange_token(const QString& api_key, const QString& api_secret,
                                         const QString& auth_code) override;

    /// Returns the URL users visit to authenticate via Zerodha's web UI.
    /// Pure string - safe from any thread.
    static QString kite_login_url(const QString& api_key);

    /// Runs the full headless TOTP auto-login chain. MUST be called from a
    /// worker thread. Internally uses QEventLoop across 4 HTTP round-trips.
    TokenExchangeResponse login_with_totp(const QString& user_id, const QString& password, const QString& api_key,
                                          const QString& api_secret, const QString& totp_secret,
                                          std::function<void(const QString&)> progress = {});

    // Silent refresh replays the stored TOTP auto-login (Kite has no retail
    // refresh token). Needs user_id + password + totp_secret in storage.
    bool supports_silent_refresh() const override { return true; }
    TokenExchangeResponse refresh_session(const BrokerCredentials& creds) override;

    OrderPlaceResponse place_order(const BrokerCredentials& creds, const UnifiedOrder& order) override;
    ApiResponse<QJsonObject> modify_order(const BrokerCredentials& creds, const QString& order_id,
                                          const QJsonObject& mods) override;
    ApiResponse<QJsonObject> cancel_order(const BrokerCredentials& creds, const QString& order_id) override;
    ApiResponse<QVector<BrokerOrderInfo>> get_orders(const BrokerCredentials& creds) override;
    ApiResponse<QJsonObject> get_trade_book(const BrokerCredentials& creds) override;
    ApiResponse<QVector<BrokerPosition>> get_positions(const BrokerCredentials& creds) override;
    ApiResponse<QVector<BrokerHolding>> get_holdings(const BrokerCredentials& creds) override;
    ApiResponse<BrokerFunds> get_funds(const BrokerCredentials& creds) override;
    ApiResponse<QVector<BrokerQuote>> get_quotes(const BrokerCredentials& creds,
                                                 const QVector<QString>& symbols) override;
    ApiResponse<QVector<BrokerCandle>> get_history(const BrokerCredentials& creds, const QString& symbol,
                                                   const QString& resolution, const QString& from_date,
                                                   const QString& to_date) override;

    // --- Multi-quote & Market Depth ---
    ApiResponse<QVector<BrokerQuote>> get_multi_quotes(const BrokerCredentials& creds,
                                                       const QVector<QPair<QString, QString>>& symbols) override;

    ApiResponse<MarketDepth> get_market_depth(const BrokerCredentials& creds, const QString& symbol,
                                              const QString& exchange) override;

    // --- Margin ---
    ApiResponse<OrderMargin> get_order_margins(const BrokerCredentials& creds, const UnifiedOrder& order) override;
    ApiResponse<BasketMargin> get_basket_margins(const BrokerCredentials& creds,
                                                 const QVector<UnifiedOrder>& orders) override;

    // --- GTT ---
    GttPlaceResponse gtt_place(const BrokerCredentials& creds, const GttOrder& order) override;
    ApiResponse<GttOrder> gtt_get(const BrokerCredentials& creds, const QString& gtt_id) override;
    ApiResponse<QVector<GttOrder>> gtt_list(const BrokerCredentials& creds) override;
    ApiResponse<GttOrder> gtt_modify(const BrokerCredentials& creds, const QString& gtt_id,
                                     const GttOrder& updated) override;
    ApiResponse<QJsonObject> gtt_cancel(const BrokerCredentials& creds, const QString& gtt_id) override;

  protected:
    QMap<QString, QString> auth_headers(const BrokerCredentials& creds) const override;

    /// Returns true if the HTTP response indicates an expired/invalid access token.
    /// Zerodha returns HTTP 403 with error_type "TokenException" for expired tokens.
    static bool is_token_expired(const BrokerHttpResponse& resp);

  private:
    static constexpr const char* kite_api_version = "3";
    /// Single source of truth for OrderType/OrderSide/ProductType → Kite wire values.
    /// Replaces the three `zerodha_order_type/side/product` switches.
    static const BrokerEnumMap<QString>& kite_enum_map();
    static const char* zerodha_variety(ProductType p);
    static QString zerodha_interval(const QString& resolution);
    static QString checked_error(const BrokerHttpResponse& resp, const QString& fallback);
};

} // namespace fincept::trading
