package jforex.experimental;

import com.dukascopy.api.*;

import java.awt.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import static java.time.Instant.ofEpochMilli;

public class ExportOrdersStrategy implements IStrategy {
    private IEngine engine;
    private IConsole console;
    private IHistory history;
    private IContext context;
    private IIndicators indicators;
    private IUserInterface userInterface;

    public void onStart(IContext context) throws JFException {
        this.engine = context.getEngine();
        this.console = context.getConsole();
        this.history = context.getHistory();
        this.context = context;
        this.indicators = context.getIndicators();
        this.userInterface = context.getUserInterface();

        StringBuilder sb = new StringBuilder(32 * 1024);
        int i = 0;
        double MLN = 1_000_000D;
        long now = System.currentTimeMillis();
        long hstFrom = now - Period.MONTHLY.getInterval() * 24;

        Instrument instr = Instrument.EURUSD; // <-- change instrument here

        try {
            List<IOrder> orders = new ArrayList<>(context.getEngine().getOrders(instr));
            orders.addAll(context.getHistory().getOrdersHistory(instr, hstFrom, now));
            for( IOrder o : orders ) {
                sb.append("\n");
                sb.append("#" + (++i) + ": ");

                sb.append(o.getLabel()).append("/").append(o.getId()).append(' ').append(o.getState()).append("\n\n");
                sb.append("    Action:       <").append(o.getOrderCommand()).append("> ").append(o.getInstrument()).append("\n");
                sb.append("    Created:      ").append(timeStrY4MDHMSF(o.getCreationTime())).append("\n");
                sb.append("    Amount:       ").append(intStr(o.getAmount() * MLN)).append("\n");
                sb.append("    Amount Req.:  ").append(intStr(o.getRequestedAmount() * MLN)).append("\n");
                sb.append("    Amount Orig.: ").append(intStr(o.getOriginalAmount() * MLN)).append("\n");
                sb.append("    Open Price:   ").append(o.getOpenPrice()).append("\n");

                List<IFillOrder> fh = o.getFillHistory();
                if( !fh.isEmpty() ) {
                    sb.append("    Fill Time:    ").append(timeStrY4MDHMSF(o.getFillTime())).append("\n");
                    sb.append("    Filled:       ");
                    for( int j = 0; j < fh.size(); j++ ) {
                        IFillOrder fo = fh.get(j);
                        if( j > 0 )
                            sb.append("                  ");
                        sb.append("#").append(j)
                                .append(": ").append(timeStrY4MDHMSF(fo.getTime()))
                                .append(" - ").append(String.format("%7s", intStr(fo.getAmount() * MLN)))
                                .append(" * ").append(fo.getPrice()).append("\n");
                    }
                }

                if( o.getStopLossPrice() != 0 )
                    sb.append("    Stop Loss:    " + o.getStopLossPrice() + " = " +
                        roundTo(1, pipsOfPrice(o, o.getStopLossPrice() - o.getOpenPrice())) + " pips" + "\n");
                if( o.getTakeProfitPrice() != 0 )
                    sb.append("    Take Profit:  " + o.getTakeProfitPrice() + " = " +
                        roundTo(1, pipsOfPrice(o, o.getTakeProfitPrice() - o.getOpenPrice())) + " pips" + "\n");

                sb.append("    Commission:   " + o.getCommissionInUSD() + " $\n");
                sb.append("    Profit$:      " + o.getProfitLossInUSD() + " $\n");
                sb.append("    Profit.:      " + o.getProfitLossInPips() + " pips\n");

                List<ICloseOrder> ch = o.getCloseHistory();
                if( !ch.isEmpty() ) {
                    sb.append("    Close Time:   " + timeStrY4MDHMSF(o.getCloseTime()) + "\n");
                    sb.append("    Close Price:  " + o.getClosePrice() + "\n");
                    sb.append("    Closed:       ");

                    for( int j = 0; j < ch.size(); j++ ) {
                        ICloseOrder co = ch.get(j);
                        if( j > 0 )
                            sb.append("                  ");
                        sb.append("#" + j
                                + ": " + timeStrY4MDHMSF(co.getTime())
                                + " - " + String.format("%7s", intStr(co.getAmount() * 1_000_000))
                                + " * " + co.getPrice() + "\n");
                    }
                }

            }

            String instrCode = instr.toString().replace("/", "");
            String nowTime = timeStrY4MDHMSF(now).replace(':', '-').replace(' ', '_').replace('.', '_');

            Path output = context.getFilesDir().toPath().resolve("orders-" + instrCode + '-' + nowTime + ".txt");
            Files.write(output, sb.toString().getBytes(StandardCharsets.UTF_8));

            Desktop.getDesktop().open(output.toFile());
        }
        catch( IOException e ) {
            e.printStackTrace(context.getConsole().getErr());
        }
    }

    public void onAccount(IAccount account) throws JFException {
    }

    public void onMessage(IMessage message) throws JFException {
    }

    public void onStop() throws JFException {
    }

    public void onTick(Instrument instrument, ITick tick) throws JFException {
    }

    public void onBar(Instrument instrument, Period period, IBar askBar, IBar bidBar) throws JFException {
    }

    //
    // Helpers
    //

    public final static DecimalFormat INT_FORMATTER = (DecimalFormat)NumberFormat.getInstance(Locale.US);

    static {
        DecimalFormatSymbols symbols = INT_FORMATTER.getDecimalFormatSymbols();
        symbols.setGroupingSeparator(' ');
        INT_FORMATTER.setDecimalFormatSymbols(symbols);
    }

    private final static String Y4MDHMSF_STR = "yyyy-MM-dd HH:mm:ss.SSS";
    private final static DateTimeFormatter DTF_Y4MDHMSF = DateTimeFormatter.ofPattern(Y4MDHMSF_STR);

    public static String intStr(Number v) {
        return v == null ? "" : INT_FORMATTER.format(v.doubleValue() + 0.000_000_5);
    }

    private static String timeStrY4MDHMSF(long time) {
        LocalDateTime local = LocalDateTime.ofInstant(ofEpochMilli(time), ZoneId.systemDefault());
        return time == 0 ? "" : DTF_Y4MDHMSF.format(local);
    }

    private static double roundTo(int digits, double price) {
        int pipScale = (int)Math.pow(10, digits);
        double rPrice = (double)Math.round(price * pipScale) / pipScale;
        return rPrice;
    }

    private static double pipsOfPrice(IOrder order, double price) {
        return price / order.getInstrument().getPipValue();
    }
}