using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using NT8.Core.Logging;

namespace NT8.Core.Analytics
{
    /// <summary>
    /// Generates performance reports and export formats.
    /// </summary>
    public class ReportGenerator
    {
        private readonly ILogger _logger;
        private readonly PerformanceCalculator _calculator;

        public ReportGenerator(ILogger logger)
        {
            if (logger == null)
                throw new ArgumentNullException("logger");

            _logger = logger;
            _calculator = new PerformanceCalculator(logger);
        }

        /// <summary>
        /// Generates daily report.
        /// </summary>
        public DailyReport GenerateDailyReport(DateTime date, List<TradeRecord> trades)
        {
            if (trades == null)
                throw new ArgumentNullException("trades");

            try
            {
                var dayStart = date.Date;
                var dayEnd = dayStart.AddDays(1);
                var subset = trades.Where(t => t.EntryTime >= dayStart && t.EntryTime < dayEnd).ToList();

                var report = new DailyReport();
                report.Date = dayStart;
                report.SummaryMetrics = _calculator.Calculate(subset);

                foreach (var g in subset.GroupBy(t => t.Grade.ToString()))
                {
                    report.GradePnL[g.Key] = g.Sum(t => t.RealizedPnL);
                }

                return report;
            }
            catch (Exception ex)
            {
                _logger.LogError("GenerateDailyReport failed: {0}", ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Generates weekly report.
        /// </summary>
        public WeeklyReport GenerateWeeklyReport(DateTime weekStart, List<TradeRecord> trades)
        {
            if (trades == null)
                throw new ArgumentNullException("trades");

            try
            {
                var start = weekStart.Date;
                var end = start.AddDays(7);
                var subset = trades.Where(t => t.EntryTime >= start && t.EntryTime < end).ToList();

                var report = new WeeklyReport();
                report.WeekStart = start;
                report.WeekEnd = end.AddTicks(-1);
                report.SummaryMetrics = _calculator.Calculate(subset);

                foreach (var g in subset.GroupBy(t => t.StrategyName ?? string.Empty))
                {
                    report.StrategyPnL[g.Key] = g.Sum(t => t.RealizedPnL);
                }

                return report;
            }
            catch (Exception ex)
            {
                _logger.LogError("GenerateWeeklyReport failed: {0}", ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Generates monthly report.
        /// </summary>
        public MonthlyReport GenerateMonthlyReport(int year, int month, List<TradeRecord> trades)
        {
            if (trades == null)
                throw new ArgumentNullException("trades");

            try
            {
                var start = new DateTime(year, month, 1);
                var end = start.AddMonths(1);
                var subset = trades.Where(t => t.EntryTime >= start && t.EntryTime < end).ToList();

                var report = new MonthlyReport();
                report.Year = year;
                report.Month = month;
                report.SummaryMetrics = _calculator.Calculate(subset);

                foreach (var g in subset.GroupBy(t => t.Symbol ?? string.Empty))
                {
                    report.SymbolPnL[g.Key] = g.Sum(t => t.RealizedPnL);
                }

                return report;
            }
            catch (Exception ex)
            {
                _logger.LogError("GenerateMonthlyReport failed: {0}", ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Exports report to text format.
        /// </summary>
        public string ExportToText(Report report)
        {
            if (report == null)
                throw new ArgumentNullException("report");

            try
            {
                var sb = new StringBuilder();
                sb.AppendLine(string.Format("=== {0} Report ===", report.ReportName));
                sb.AppendLine(string.Format("Generated: {0:O}", report.GeneratedAtUtc));
                sb.AppendLine();
                sb.AppendLine(string.Format("Total Trades: {0}", report.SummaryMetrics.TotalTrades));
                sb.AppendLine(string.Format("Win Rate: {0:P2}", report.SummaryMetrics.WinRate));
                sb.AppendLine(string.Format(CultureInfo.InvariantCulture, "Net Profit: {0:F2}", report.SummaryMetrics.NetProfit));
                sb.AppendLine(string.Format(CultureInfo.InvariantCulture, "Profit Factor: {0:F2}", report.SummaryMetrics.ProfitFactor));
                sb.AppendLine(string.Format(CultureInfo.InvariantCulture, "Expectancy: {0:F2}", report.SummaryMetrics.Expectancy));
                sb.AppendLine(string.Format(CultureInfo.InvariantCulture, "Max Drawdown %: {0:F2}", report.SummaryMetrics.MaxDrawdownPercent));
                return sb.ToString();
            }
            catch (Exception ex)
            {
                _logger.LogError("ExportToText failed: {0}", ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Exports trade records to CSV.
        /// </summary>
        public string ExportToCsv(List<TradeRecord> trades)
        {
            if (trades == null)
                throw new ArgumentNullException("trades");

            try
            {
                var sb = new StringBuilder();
                sb.AppendLine("TradeId,Symbol,Strategy,EntryTime,ExitTime,Side,Qty,Entry,Exit,PnL,RMultiple,Grade,RiskMode");

                foreach (var t in trades.OrderBy(x => x.EntryTime))
                {
                    sb.AppendFormat(CultureInfo.InvariantCulture,
                        "{0},{1},{2},{3:O},{4},{5},{6},{7:F4},{8},{9:F2},{10:F4},{11},{12}",
                        Escape(t.TradeId),
                        Escape(t.Symbol),
                        Escape(t.StrategyName),
                        t.EntryTime,
                        t.ExitTime.HasValue ? t.ExitTime.Value.ToString("O") : string.Empty,
                        t.Side,
                        t.Quantity,
                        t.EntryPrice,
                        t.ExitPrice.HasValue ? t.ExitPrice.Value.ToString("F4", CultureInfo.InvariantCulture) : string.Empty,
                        t.RealizedPnL,
                        t.RMultiple,
                        t.Grade,
                        t.RiskMode);
                    sb.AppendLine();
                }

                return sb.ToString();
            }
            catch (Exception ex)
            {
                _logger.LogError("ExportToCsv failed: {0}", ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Exports report summary to JSON.
        /// </summary>
        public string ExportToJson(Report report)
        {
            if (report == null)
                throw new ArgumentNullException("report");

            try
            {
                var json = new StringBuilder();
                json.Append("{");
                json.AppendFormat(CultureInfo.InvariantCulture, "\"reportName\":\"{0}\"", EscapeJson(report.ReportName));
                json.AppendFormat(CultureInfo.InvariantCulture, ",\"generatedAtUtc\":\"{0:O}\"", report.GeneratedAtUtc);
                json.Append(",\"summary\":{");
                json.AppendFormat(CultureInfo.InvariantCulture, "\"totalTrades\":{0}", report.SummaryMetrics.TotalTrades);
                json.AppendFormat(CultureInfo.InvariantCulture, ",\"winRate\":{0}", report.SummaryMetrics.WinRate);
                json.AppendFormat(CultureInfo.InvariantCulture, ",\"netProfit\":{0}", report.SummaryMetrics.NetProfit);
                json.AppendFormat(CultureInfo.InvariantCulture, ",\"profitFactor\":{0}", report.SummaryMetrics.ProfitFactor);
                json.AppendFormat(CultureInfo.InvariantCulture, ",\"expectancy\":{0}", report.SummaryMetrics.Expectancy);
                json.Append("}");
                json.Append("}");
                return json.ToString();
            }
            catch (Exception ex)
            {
                _logger.LogError("ExportToJson failed: {0}", ex.Message);
                throw;
            }
        }

        /// <summary>
        /// Builds equity curve points from realized pnl.
        /// </summary>
        public EquityCurve BuildEquityCurve(List<TradeRecord> trades)
        {
            if (trades == null)
                throw new ArgumentNullException("trades");

            try
            {
                var curve = new EquityCurve();
                var equity = 0.0;
                var peak = 0.0;

                foreach (var trade in trades.OrderBy(t => t.ExitTime.HasValue ? t.ExitTime.Value : t.EntryTime))
                {
                    equity += trade.RealizedPnL;
                    if (equity > peak)
                        peak = equity;

                    var point = new EquityPoint();
                    point.Time = trade.ExitTime.HasValue ? trade.ExitTime.Value : trade.EntryTime;
                    point.Equity = equity;
                    point.Drawdown = peak - equity;
                    curve.Points.Add(point);
                }

                return curve;
            }
            catch (Exception ex)
            {
                _logger.LogError("BuildEquityCurve failed: {0}", ex.Message);
                throw;
            }
        }

        private static string Escape(string value)
        {
            if (value == null)
                return string.Empty;

            if (value.Contains(",") || value.Contains("\"") || value.Contains("\n") || value.Contains("\r"))
                return string.Format("\"{0}\"", value.Replace("\"", "\"\""));
            return value;
        }

        private static string EscapeJson(string value)
        {
            if (value == null)
                return string.Empty;

            return value.Replace("\\", "\\\\").Replace("\"", "\\\"");
        }
    }
}
