using ScadaLink.Commons.Entities.Sites;
using ScadaLink.Commons.Interfaces.Repositories;
using ScadaLink.Commons.Interfaces.Services;
using ScadaLink.Commons.Types;

namespace ScadaLink.TemplateEngine.Services;

/// <summary>
/// Site and data connection management.
/// - Site CRUD (name, identifier, description)
/// - Data connection CRUD (name, protocol, config) — each connection belongs to exactly one site
/// - Audit logging
/// </summary>
public class SiteService
{
    private readonly ISiteRepository _repository;
    private readonly IAuditService _auditService;

    public SiteService(ISiteRepository repository, IAuditService auditService)
    {
        _repository = repository ?? throw new ArgumentNullException(nameof(repository));
        _auditService = auditService ?? throw new ArgumentNullException(nameof(auditService));
    }

    // --- Site CRUD ---

    public async Task<Result<Site>> CreateSiteAsync(
        string name, string siteIdentifier, string? description, string user,
        CancellationToken cancellationToken = default)
    {
        if (string.IsNullOrWhiteSpace(name))
            return Result<Site>.Failure("Site name is required.");
        if (string.IsNullOrWhiteSpace(siteIdentifier))
            return Result<Site>.Failure("Site identifier is required.");

        var existing = await _repository.GetSiteByIdentifierAsync(siteIdentifier, cancellationToken);
        if (existing != null)
            return Result<Site>.Failure($"Site with identifier '{siteIdentifier}' already exists.");

        var site = new Site(name, siteIdentifier) { Description = description };
        await _repository.AddSiteAsync(site, cancellationToken);
        await _repository.SaveChangesAsync(cancellationToken);

        await _auditService.LogAsync(user, "Create", "Site", site.Id.ToString(), name, site, cancellationToken);

        return Result<Site>.Success(site);
    }

    public async Task<Result<Site>> UpdateSiteAsync(
        int siteId, string name, string? description, string user,
        CancellationToken cancellationToken = default)
    {
        var site = await _repository.GetSiteByIdAsync(siteId, cancellationToken);
        if (site == null)
            return Result<Site>.Failure($"Site with ID {siteId} not found.");

        site.Name = name;
        site.Description = description;
        await _repository.UpdateSiteAsync(site, cancellationToken);
        await _repository.SaveChangesAsync(cancellationToken);

        await _auditService.LogAsync(user, "Update", "Site", site.Id.ToString(), name, site, cancellationToken);

        return Result<Site>.Success(site);
    }

    public async Task<Result<bool>> DeleteSiteAsync(int siteId, string user, CancellationToken cancellationToken = default)
    {
        var site = await _repository.GetSiteByIdAsync(siteId, cancellationToken);
        if (site == null)
            return Result<bool>.Failure($"Site with ID {siteId} not found.");

        // Check for instances assigned to this site
        var instances = await _repository.GetInstancesBySiteIdAsync(siteId, cancellationToken);
        if (instances.Count > 0)
        {
            var names = string.Join(", ", instances.Select(i => i.UniqueName).Take(5));
            return Result<bool>.Failure(
                $"Cannot delete site '{site.Name}': {instances.Count} instance(s) are assigned to it ({names}{(instances.Count > 5 ? "..." : "")}).");
        }

        await _repository.DeleteSiteAsync(siteId, cancellationToken);
        await _repository.SaveChangesAsync(cancellationToken);

        await _auditService.LogAsync(user, "Delete", "Site", siteId.ToString(), site.Name, null, cancellationToken);

        return Result<bool>.Success(true);
    }

    public async Task<Site?> GetSiteByIdAsync(int siteId, CancellationToken cancellationToken = default) =>
        await _repository.GetSiteByIdAsync(siteId, cancellationToken);

    public async Task<IReadOnlyList<Site>> GetAllSitesAsync(CancellationToken cancellationToken = default) =>
        await _repository.GetAllSitesAsync(cancellationToken);

    // --- Data Connection CRUD ---

    public async Task<Result<DataConnection>> CreateDataConnectionAsync(
        int siteId, string name, string protocol, string? primaryConfiguration,
        string? backupConfiguration, int failoverRetryCount, string user,
        CancellationToken cancellationToken = default)
    {
        if (string.IsNullOrWhiteSpace(name))
            return Result<DataConnection>.Failure("Data connection name is required.");
        if (string.IsNullOrWhiteSpace(protocol))
            return Result<DataConnection>.Failure("Protocol is required.");

        var connection = new DataConnection(name, protocol, siteId)
        {
            PrimaryConfiguration = primaryConfiguration,
            BackupConfiguration = backupConfiguration,
            FailoverRetryCount = failoverRetryCount
        };
        await _repository.AddDataConnectionAsync(connection, cancellationToken);
        await _repository.SaveChangesAsync(cancellationToken);

        await _auditService.LogAsync(user, "Create", "DataConnection",
            connection.Id.ToString(), name, connection, cancellationToken);

        return Result<DataConnection>.Success(connection);
    }

    public async Task<Result<DataConnection>> UpdateDataConnectionAsync(
        int connectionId, string name, string protocol, string? primaryConfiguration,
        string? backupConfiguration, int failoverRetryCount, string user,
        CancellationToken cancellationToken = default)
    {
        var connection = await _repository.GetDataConnectionByIdAsync(connectionId, cancellationToken);
        if (connection == null)
            return Result<DataConnection>.Failure($"Data connection with ID {connectionId} not found.");

        connection.Name = name;
        connection.Protocol = protocol;
        connection.PrimaryConfiguration = primaryConfiguration;
        connection.BackupConfiguration = backupConfiguration;
        connection.FailoverRetryCount = failoverRetryCount;
        await _repository.UpdateDataConnectionAsync(connection, cancellationToken);
        await _repository.SaveChangesAsync(cancellationToken);

        await _auditService.LogAsync(user, "Update", "DataConnection",
            connection.Id.ToString(), name, connection, cancellationToken);

        return Result<DataConnection>.Success(connection);
    }

    public async Task<Result<bool>> DeleteDataConnectionAsync(
        int connectionId, string user, CancellationToken cancellationToken = default)
    {
        var connection = await _repository.GetDataConnectionByIdAsync(connectionId, cancellationToken);
        if (connection == null)
            return Result<bool>.Failure($"Data connection with ID {connectionId} not found.");

        await _repository.DeleteDataConnectionAsync(connectionId, cancellationToken);
        await _repository.SaveChangesAsync(cancellationToken);

        await _auditService.LogAsync(user, "Delete", "DataConnection",
            connectionId.ToString(), connection.Name, null, cancellationToken);

        return Result<bool>.Success(true);
    }
}
