using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Repositories;
using Managing.Application.Abstractions.Services;
using Managing.Application.Workflows.Flows.Feeds;
using Managing.Application.Workflows.Flows.Strategies;
using Managing.Application.Workflows.Flows.Trading;
using Managing.Domain.Workflows;
using Managing.Domain.Workflows.Synthetics;

namespace Managing.Application.Workflows;

public class WorkflowService : IWorkflowService
{
    private readonly IWorkflowRepository _workflowRepository;
    private readonly IExchangeService _exchangeService;
    private readonly IFlowFactory _flowFactory;
    private readonly ICacheService _cacheService;
    private readonly ITradingService _tradingService;
    private readonly IAccountService _accountService;

    public WorkflowService(
        IWorkflowRepository workflowRepository,
        IExchangeService exchangeService,
        IFlowFactory flowFactory,
        ICacheService cacheService,
        ITradingService tradingService,
        IAccountService accountService)
    {
        _workflowRepository = workflowRepository;
        _exchangeService = exchangeService;
        _flowFactory = flowFactory;
        _cacheService = cacheService;
        _tradingService = tradingService;
        _accountService = accountService;
    }

    public async Task<Workflow> InsertOrUpdateWorkflow(SyntheticWorkflow workflowRequest)
    {
        var previousWorkflow = await _workflowRepository.GetWorkflow(workflowRequest.Name);

        try
        {
            if (previousWorkflow != null)
            {
                await _workflowRepository.UpdateWorkflow(workflowRequest);
            }
            else
            {
                await _workflowRepository.InsertWorkflow(workflowRequest);
            }
        }
        catch (Exception ex)
        {
            throw;
        }

        return Map(workflowRequest);
    }

    private Workflow Map(SyntheticWorkflow workflowRequest)
    {
        var workflow = new Workflow
        {
            Name = workflowRequest.Name,
            Usage = workflowRequest.Usage,
            Description = workflowRequest.Description,
            Flows = new List<IFlow>()
        };

        // Add first flow that dont have any parent
        var firstFlow = workflowRequest.Flows.SingleOrDefault(f => string.IsNullOrEmpty(f.ParentId));

        if (firstFlow != null)
        {
            var flow = Build(workflowRequest.Flows, firstFlow);
            workflow.Flows.Add(flow);
        }
        else
        {
            // TODO : Throw exception
            throw new Exception("No first flow found");
        }

        return workflow;
    }

    private IFlow Build(List<SyntheticFlow> flows, SyntheticFlow firstFlow)
    {
        var flow = _flowFactory.BuildFlow(firstFlow);

        foreach (var flowRequest in flows.Where(f => f.ParentId == firstFlow.Id))
        {
            flow.Children.Add(Build(flows, flowRequest));
        }

        return flow;
    }

    public IEnumerable<SyntheticWorkflow> GetWorkflows()
    {
        return _workflowRepository.GetWorkflows();
    }

    public bool DeleteWorkflow(string name)
    {
        return _workflowRepository.DeleteWorkflow(name);
    }

    public async Task<Workflow> GetWorkflow(string name)
    {
        return Map(await _workflowRepository.GetWorkflow(name));
    }

    public Task<IEnumerable<IFlow>> GetAvailableFlows()
    {
        var availableFlows = new List<IFlow>
        {
            new FeedTicker(_exchangeService),
            new RsiDiv(),
            new OpenPosition(_exchangeService, _cacheService, _accountService, _tradingService)
        };

        return Task.FromResult(availableFlows.AsEnumerable());
    }
}