using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using OI.Metrology.Archive.Models;
using OI.Metrology.Shared.DataModels;
using OI.Metrology.Shared.Models.Stateless;
using OI.Metrology.Shared.Services;
using System;
using System.Collections.Generic;
using System.Linq;

namespace OI.Metrology.Archive.ApiControllers;

[ApiController]
public class InboundController : ControllerBase
{
    private readonly ILogger _Logger;
    private readonly AppSettings _AppSettings;
    private readonly IAttachmentsService _AttachmentService;
    private readonly IInboundDataService _InboundDataService;
    private readonly IMetrologyRepository _MetrologyRepository;

    public InboundController(AppSettings appSettings, ILogger<InboundController> logger, IMetrologyRepository metrologyRepository, IInboundDataService inboundDataService, IAttachmentsService attachmentService)
    {
        _Logger = logger;
        _AppSettings = appSettings;
        _AttachmentService = attachmentService;
        _InboundDataService = inboundDataService;
        _MetrologyRepository = metrologyRepository;
    }

    // this class represents the API response back to the client
    public class DataResponse
    {
        public bool Success { get; set; }
        public long HeaderID { get; set; }
        public List<string>? Errors { get; set; }
        public List<string>? Warnings { get; set; }
    }

    // this is the main endpoint, it accepts a JSON message that contains both the header and data records together
    // tooltype is the ToolTypeName column from the ToolType table
    // JToken is how you can accept a JSON message without deserialization.
    // Using "string" doesn't work because ASP.NET Core will expect a json encoded string, not give you the actual string.
    [HttpPost("/api/inbound/{tooltype}")]
    public IActionResult Data(string tooltype, [FromBody] JToken jsonbody)
    {
        DataResponse r = new()
        {
            Success = false,
            HeaderID = -1,
            Errors = new List<string>(),
            Warnings = new List<string>()
        };

        if (!IsIPAddressAllowed())
        {
            _Logger.LogInformation($"Rejected remote IP: {HttpContext.Connection.RemoteIpAddress}");
            r.Errors.Add("Remote IP is not on allowed list");
            return Unauthorized(r);
        }

        ToolType toolType = _MetrologyRepository.GetToolTypeByName(tooltype);

        if (toolType is null)
        {
            r.Errors.Add("Invalid tool type: " + tooltype);
            return BadRequest(r);
        }

        // get metadata

        List<ToolTypeMetadata> metaData = _MetrologyRepository.GetToolTypeMetadataByToolTypeID(toolType.ID).ToList();

        if (metaData is null)
        {
            r.Errors.Add("Invalid metadata for tool type: " + tooltype);
            return BadRequest(r);
        }

        // validate fields

        if (jsonbody is not null)
            _InboundDataService.ValidateJSONFields(jsonbody, 0, metaData, r.Errors, r.Warnings);
        else
            r.Errors.Add("Invalid json");

        if (r.Errors.Count == 0 && jsonbody is not null)
        {
            try
            {
                r.HeaderID = _InboundDataService.DoSQLInsert(jsonbody, toolType, metaData);
                r.Success = r.HeaderID > 0;
            }
            catch (Exception ex)
            {
                r.Errors.Add(ex.Message);
            }
            return Ok(r);
        }
        else
        {
            return BadRequest(r);
        }

    }

    // this is the endpoint for attaching a field.  It is not JSON, it is form-data/multipart like an HTML form because that's the normal way.
    // header ID is the ID value from the Header table
    // datauniqueid is the Title value from the Data/Detail table
    [HttpPost("/api/inbound/{tooltype}/attachment")]
    public IActionResult AttachFile(string tooltype, [FromQuery] long headerid, [FromQuery] string datauniqueid = "")
    {
        if (!IsIPAddressAllowed())
        {
            _Logger.LogInformation($"Rejected remote IP: {HttpContext.Connection.RemoteIpAddress}");
            return Unauthorized("Remote IP is not on allowed list");
        }

        ToolType toolType = _MetrologyRepository.GetToolTypeByName(tooltype);

        if (toolType is null)
            return BadRequest($"Invalid tool type: {tooltype}");

        if (Request.Form is null)
            return BadRequest($"Invalid form");

        if (Request.Form.Files.Count != 1)
            return BadRequest($"Invalid file count");

        string filename = System.IO.Path.GetFileName(Request.Form.Files[0].FileName);

        if (string.IsNullOrWhiteSpace(filename))
            return BadRequest("Empty filename");

        if (filename.IndexOfAny(System.IO.Path.GetInvalidFileNameChars()) >= 0)
            return BadRequest("Invalid filename");

        _AttachmentService.SaveAttachment(toolType, headerid, datauniqueid, filename, Request.Form.Files[0]);

        return Ok();
    }

    protected bool IsIPAddressAllowed()
    {
        if (string.IsNullOrWhiteSpace(_AppSettings.InboundApiAllowedIPList))
            return true;

        System.Net.IPAddress? remoteIP = HttpContext.Connection.RemoteIpAddress;
        byte[]? remoteIPBytes = remoteIP?.GetAddressBytes();

        string[] allowedIPs = _AppSettings.InboundApiAllowedIPList.Split(';');
        foreach (string ip in allowedIPs)
        {
            System.Net.IPAddress? parsedIP;
            if (remoteIPBytes is not null && System.Net.IPAddress.TryParse(ip, out parsedIP))
            {
                if (parsedIP.GetAddressBytes().SequenceEqual(remoteIPBytes))
                    return true;
            }
        }

        return false;
    }

}