using System;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;

namespace cAlgo.Robots
{
    public enum TradingDirection
    {
        Both,
        LongOnly,
        ShortOnly
    }

    public enum PendingSignalState
    {
        None,
        WaitingForBuyEntry,
        WaitingForSellEntry
    }

    // Continuous Trend-State Bot with risk management and SMA distance exit.
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
    public class SmaHmaBot : Robot
    {
        #region Parameters

        [Parameter("Risk Percentage (%)", DefaultValue = 2.0, MinValue = 0.1, MaxValue = 10.0)]
        public double RiskPercentage { get; set; }

        [Parameter("Min SL Distance (Pips)", DefaultValue = 40, MinValue = 1)]
        public double MinStopLossPips { get; set; }

        [Parameter("Max SMA Distance (Pips)", DefaultValue = 100, MinValue = 1)]
        public double MaxSmaDistancePips { get; set; }

        [Parameter("SMA Period", DefaultValue = 200, MinValue = 1)]
        public int SmaPeriod { get; set; }

        [Parameter("HMA Period", DefaultValue = 250, MinValue = 1)]
        public int HmaPeriod { get; set; }

        [Parameter("Trading Direction", DefaultValue = TradingDirection.Both)]
        public TradingDirection AllowedDirection { get; set; }

        [Parameter("BE Offset (Pips)", DefaultValue = 0.5, MinValue = 0)]
        public double BreakEvenOffsetPips { get; set; }

        [Parameter("Telegram Bot Token", DefaultValue = "")]
        public string TelegramBotToken { get; set; }

        [Parameter("Telegram Chat ID", DefaultValue = "")]
        public string TelegramChatId { get; set; }

        #endregion

        #region Fields

        private SimpleMovingAverage _sma;
        private HullMovingAverage _hma;
        private HttpClient _httpClient;

        private PendingSignalState _signalState = PendingSignalState.None;
        private double _extremumSinceSignal;
        private const string PositionLabel = "SmaHmaContinuousBot";

        #endregion

        protected override void OnStart()
        {
            _sma = Indicators.SimpleMovingAverage(Bars.ClosePrices, SmaPeriod);
            _hma = Indicators.HullMovingAverage(Bars.ClosePrices, HmaPeriod);
            _httpClient = new HttpClient();

            Print("Bot started. Max SMA Distance: {0} pips", MaxSmaDistancePips);
        }

        protected override void OnStop()
        {
            _httpClient?.Dispose();
        }

        protected override void OnTick()
        {
            ManageBreakEven();
            ManageProfitTaking();
        }

        protected override void OnBarClosed()
        {
            if (Bars.Count <= Math.Max(SmaPeriod, HmaPeriod) + 2) return;

            UpdateTrendAndSignalState();
            HandleEntryLogic();
        }

        private void UpdateTrendAndSignalState()
        {
            double smaClosed = _sma.Result.Last(0);
            double smaPrev = _sma.Result.Last(1);
            double hmaClosed = _hma.Result.Last(0);
            
            bool isSmaRising = (smaClosed > smaPrev);
            bool isSmaFalling = (smaClosed < smaPrev);
            bool isHmaAboveSma = (hmaClosed > smaClosed);
            bool isHmaBelowSma = (hmaClosed < smaClosed);

            var activePosition = Positions.Find(PositionLabel, SymbolName);

            if ((activePosition != null))
            {
                if (((activePosition.TradeType == TradeType.Buy) && (isHmaBelowSma)) || 
                    ((activePosition.TradeType == TradeType.Sell) && (isHmaAboveSma)))
                {
                    ClosePosition(activePosition);
                    _signalState = PendingSignalState.None;
                }
            }

            if (activePosition == null)
            {
                if ((AllowedDirection != TradingDirection.ShortOnly) && (isHmaAboveSma) && (isSmaRising))
                {
                    if (_signalState != PendingSignalState.WaitingForBuyEntry)
                    {
                        _signalState = PendingSignalState.WaitingForBuyEntry;
                        _extremumSinceSignal = Bars.LowPrices.Last(0);
                    }
                }
                else if ((AllowedDirection != TradingDirection.LongOnly) && (isHmaBelowSma) && (isSmaFalling))
                {
                    if (_signalState != PendingSignalState.WaitingForSellEntry)
                    {
                        _signalState = PendingSignalState.WaitingForSellEntry;
                        _extremumSinceSignal = Bars.HighPrices.Last(0);
                    }
                }
                else
                {
                    _signalState = PendingSignalState.None;
                }
            }
        }

        private void HandleEntryLogic()
        {
            if (_signalState == PendingSignalState.None) return;

            double currentClose = Bars.ClosePrices.Last(0);
            double smaValue = _sma.Result.Last(0);

            if (_signalState == PendingSignalState.WaitingForBuyEntry)
            {
                _extremumSinceSignal = Math.Min(_extremumSinceSignal, Bars.LowPrices.Last(0));
                if ((Bars.HighPrices.Last(2) < Bars.LowPrices.Last(0)) && (currentClose <= smaValue))
                {
                    ExecuteEntry(TradeType.Buy, _extremumSinceSignal);
                }
            }
            else if (_signalState == PendingSignalState.WaitingForSellEntry)
            {
                _extremumSinceSignal = Math.Max(_extremumSinceSignal, Bars.HighPrices.Last(0));
                if ((Bars.LowPrices.Last(2) > Bars.HighPrices.Last(0)) && (currentClose >= smaValue))
                {
                    ExecuteEntry(TradeType.Sell, _extremumSinceSignal);
                }
            }
        }

        private void ExecuteEntry(TradeType type, double stopLossPrice)
        {
            double entryPrice = (type == TradeType.Buy) ? Symbol.Ask : Symbol.Bid;
            double slDistPips = Math.Abs(entryPrice - stopLossPrice) / Symbol.PipSize;

            if (slDistPips < MinStopLossPips)
            {
                slDistPips = MinStopLossPips;
                stopLossPrice = (type == TradeType.Buy) 
                    ? (entryPrice - MinStopLossPips * Symbol.PipSize) 
                    : (entryPrice + MinStopLossPips * Symbol.PipSize);
            }

            double riskAmount = Account.Balance * (RiskPercentage / 100.0);
            double volume = Symbol.VolumeForFixedRisk(riskAmount, slDistPips);
            volume = Symbol.NormalizeVolumeInUnits(volume, RoundingMode.ToNearest);

            if (volume < Symbol.VolumeInUnitsMin) return;

            _signalState = PendingSignalState.None;
            var result = ExecuteMarketOrder(type, SymbolName, volume, PositionLabel, null, null);

            if (result.IsSuccessful)
            {
                ModifyPosition(result.Position, stopLossPrice, null, ProtectionType.Absolute);
                TriggerNotification(type.ToString().ToUpper() + " ENTRY", $"Vol: {volume}, SL: {stopLossPrice}");
            }
        }

        private void ManageProfitTaking()
        {
            foreach (var position in Positions.Where(p => p.Label == PositionLabel))
            {
                double smaValue = _sma.Result.Last(0);
                double currentPrice = (position.TradeType == TradeType.Buy) ? Symbol.Bid : Symbol.Ask;
                double smaDistPips = Math.Abs(currentPrice - smaValue) / Symbol.PipSize;

                // Close if position in profit AND too far from SMA
                if ((position.GrossProfit > 0) && (smaDistPips > MaxSmaDistancePips))
                {
                    ClosePosition(position);
                    TriggerNotification("TAKE PROFIT", $"Price far from SMA ({smaDistPips:F1} pips)");
                }
            }
        }

        private void ManageBreakEven()
        {
            foreach (var position in Positions.Where(p => p.Label == PositionLabel))
            {
                if (position.StopLoss == null) continue;

                double initialRisk = Math.Abs(position.EntryPrice - position.StopLoss.Value);
                double currentProfitDist = (position.TradeType == TradeType.Buy) ? (Symbol.Bid - position.EntryPrice) : (position.EntryPrice - Symbol.Ask);

                if (currentProfitDist > initialRisk)
                {
                    double targetSl = (position.TradeType == TradeType.Buy) 
                        ? (position.EntryPrice + BreakEvenOffsetPips * Symbol.PipSize) 
                        : (position.EntryPrice - BreakEvenOffsetPips * Symbol.PipSize);

                    bool isAlreadyBE = (position.TradeType == TradeType.Buy) ? (position.StopLoss >= targetSl) : (position.StopLoss <= targetSl);

                    if (!isAlreadyBE) ModifyPosition(position, targetSl, null, ProtectionType.Absolute);
                }
            }
        }

        private void TriggerNotification(string type, string reason)
        {
            if (IsBacktesting) return;
            string msg = $"<b>{Symbol.Name}</b>\n{type}\n{reason}";
            Task.Run(() => SendTelegramMessageAsync(msg));
        }

        private async Task SendTelegramMessageAsync(string message)
        {
            if (string.IsNullOrWhiteSpace(TelegramBotToken)) return;
            try
            {
                string url = $"https://api.telegram.org/bot{TelegramBotToken}/sendMessage?chat_id={TelegramChatId}&text={Uri.EscapeDataString(message)}&parse_mode=HTML";
                await _httpClient.GetAsync(url);
            }
            catch { }
        }
    }
}