using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Haoliang.Models.DataCollection;
using Haoliang.Data;
using Haoliang.Data.Repositories;
using Haoliang.Data.Entities;

namespace Haoliang.Data.Repositories
{
    public interface ICollectionTaskRepository : IRepository<CollectionTask>
    {
        Task<IEnumerable<CollectionTask>> GetPendingTasksAsync();
        Task<IEnumerable<CollectionTask>> GetRunningTasksAsync();
        Task<IEnumerable<CollectionTask>> GetTasksByDeviceIdAsync(int deviceId);
        Task<IEnumerable<CollectionTask>> GetFailedTasksAsync(int retryCount = 3);
        Task<CollectionTask> GetNextPendingTaskAsync();
        Task<bool> MarkTaskCompletedAsync(int taskId, bool isSuccess, string errorMessage = null);
        Task<bool> MarkTaskRunningAsync(int taskId);
        Task<int> CountPendingTasksAsync();
        Task<int> CountFailedTasksAsync();
        Task<decimal> GetTaskSuccessRateAsync(int hours = 24);
    }

    public class CollectionTaskRepository : Repository<CollectionTask>, ICollectionTaskRepository
    {
        private readonly CNCDbContext _context;

        public CollectionTaskRepository(CNCDbContext context) : base(context)
        {
            _context = context;
        }

        public async Task<IEnumerable<CollectionTask>> GetPendingTasksAsync()
        {
            return await _context.CollectionTasks
                .Where(ct => ct.Status == "Pending")
                .OrderBy(ct => ct.ScheduledTime)
                .ToListAsync();
        }

        public async Task<IEnumerable<CollectionTask>> GetRunningTasksAsync()
        {
            return await _context.CollectionTasks
                .Where(ct => ct.Status == "Running")
                .OrderBy(ct => ct.StartTime)
                .ToListAsync();
        }

        public async Task<IEnumerable<CollectionTask>> GetTasksByDeviceIdAsync(int deviceId)
        {
            return await _context.CollectionTasks
                .Where(ct => ct.DeviceId == deviceId)
                .OrderByDescending(ct => ct.ScheduledTime)
                .ToListAsync();
        }

        public async Task<IEnumerable<CollectionTask>> GetFailedTasksAsync(int retryCount = 3)
        {
            return await _context.CollectionTasks
                .Where(ct => ct.Status == "Failed" && ct.RetryCount >= retryCount)
                .OrderByDescending(ct => ct.ScheduledTime)
                .ToListAsync();
        }

        public async Task<CollectionTask> GetNextPendingTaskAsync()
        {
            return await _context.CollectionTasks
                .Where(ct => ct.Status == "Pending" && ct.ScheduledTime <= DateTime.Now)
                .OrderBy(ct => ct.ScheduledTime)
                .FirstOrDefaultAsync();
        }

        public async Task<bool> MarkTaskCompletedAsync(int taskId, bool isSuccess, string errorMessage = null)
        {
            var task = await GetByIdAsync(taskId);
            if (task != null)
            {
                task.Status = isSuccess ? "Completed" : "Failed";
                task.EndTime = DateTime.Now;
                task.IsSuccess = isSuccess;
                
                if (!isSuccess)
                {
                    task.ErrorMessage = errorMessage;
                    task.RetryCount++;
                }

                Update(task);
                await SaveAsync();
                return true;
            }
            return false;
        }

        public async Task<bool> MarkTaskRunningAsync(int taskId)
        {
            var task = await GetByIdAsync(taskId);
            if (task != null)
            {
                task.Status = "Running";
                task.StartTime = DateTime.Now;
                
                Update(task);
                await SaveAsync();
                return true;
            }
            return false;
        }

        public async Task<int> CountPendingTasksAsync()
        {
            return await _context.CollectionTasks
                .CountAsync(ct => ct.Status == "Pending");
        }

        public async Task<int> CountFailedTasksAsync()
        {
            return await _context.CollectionTasks
                .CountAsync(ct => ct.Status == "Failed");
        }

        public async Task<decimal> GetTaskSuccessRateAsync(int hours = 24)
        {
            var startTime = DateTime.Now.AddHours(-hours);
            var tasks = await _context.CollectionTasks
                .Where(ct => ct.ScheduledTime >= startTime)
                .ToListAsync();

            if (tasks.Any())
            {
                var successCount = tasks.Count(ct => ct.IsSuccess);
                return (decimal)successCount / tasks.Count * 100;
            }

            return 0;
        }
    }

    public interface ICollectionResultRepository : IRepository<CollectionResult>
    {
        Task<IEnumerable<CollectionResult>> GetResultsByDeviceIdAsync(int deviceId);
        Task<IEnumerable<CollectionResult>> GetResultsByDateRangeAsync(DateTime startDate, DateTime endDate);
        Task<IEnumerable<CollectionResult>> GetFailedResultsAsync(int deviceId = 0);
        Task<CollectionResult> GetLatestResultAsync(int deviceId);
        Task<int> CountResultsAsync(int deviceId = 0);
        Task<int> CountFailedResultsAsync(int deviceId = 0);
        Task<decimal> GetAverageResponseTimeAsync(int deviceId = 0);
        Task<long> GetTotalDataSizeAsync(int deviceId = 0);
        Task<bool> DeleteOldResultsAsync(int keepDays = 30);
    }

    public class CollectionResultRepository : Repository<CollectionResult>, ICollectionResultRepository
    {
        private readonly CNCDbContext _context;

        public CollectionResultRepository(CNCDbContext context) : base(context)
        {
            _context = context;
        }

        public async Task<IEnumerable<CollectionResult>> GetResultsByDeviceIdAsync(int deviceId)
        {
            return await _context.CollectionResults
                .Where(cr => cr.DeviceId == deviceId)
                .OrderByDescending(cr => cr.CollectionTime)
                .ToListAsync();
        }

        public async Task<IEnumerable<CollectionResult>> GetResultsByDateRangeAsync(DateTime startDate, DateTime endDate)
        {
            return await _context.CollectionResults
                .Where(cr => cr.CollectionTime >= startDate && cr.CollectionTime <= endDate)
                .OrderByDescending(cr => cr.CollectionTime)
                .ToListAsync();
        }

        public async Task<IEnumerable<CollectionResult>> GetFailedResultsAsync(int deviceId = 0)
        {
            var query = _context.CollectionResults.Where(cr => !cr.IsSuccess);
            
            if (deviceId > 0)
            {
                query = query.Where(cr => cr.DeviceId == deviceId);
            }

            return await query
                .OrderByDescending(cr => cr.CollectionTime)
                .ToListAsync();
        }

        public async Task<CollectionResult> GetLatestResultAsync(int deviceId)
        {
            return await _context.CollectionResults
                .Where(cr => cr.DeviceId == deviceId)
                .OrderByDescending(cr => cr.CollectionTime)
                .FirstOrDefaultAsync();
        }

        public async Task<int> CountResultsAsync(int deviceId = 0)
        {
            var query = _context.CollectionResults.AsQueryable();
            
            if (deviceId > 0)
            {
                query = query.Where(cr => cr.DeviceId == deviceId);
            }

            return await query.CountAsync();
        }

        public async Task<int> CountFailedResultsAsync(int deviceId = 0)
        {
            var query = _context.CollectionResults.Where(cr => !cr.IsSuccess);
            
            if (deviceId > 0)
            {
                query = query.Where(cr => cr.DeviceId == deviceId);
            }

            return await query.CountAsync();
        }

        public async Task<decimal> GetAverageResponseTimeAsync(int deviceId = 0)
        {
            var query = _context.CollectionResults.Where(cr => cr.IsSuccess);
            
            if (deviceId > 0)
            {
                query = query.Where(cr => cr.DeviceId == deviceId);
            }

            var results = await query.ToListAsync();
            
            if (results.Any())
            {
                return (decimal)results.Average(cr => cr.ResponseTime);
            }

            return 0;
        }

        public async Task<long> GetTotalDataSizeAsync(int deviceId = 0)
        {
            var query = _context.CollectionResults.Where(cr => cr.IsSuccess);
            
            if (deviceId > 0)
            {
                query = query.Where(cr => cr.DeviceId == deviceId);
            }

            var results = await query.ToListAsync();
            
            return results.Sum(cr => cr.DataSize ?? 0);
        }

        public async Task<bool> DeleteOldResultsAsync(int keepDays = 30)
        {
            var cutoffDate = DateTime.Now.AddDays(-keepDays);
            var oldResults = await _context.CollectionResults
                .Where(cr => cr.CollectionTime < cutoffDate)
                .ToListAsync();

            if (oldResults.Any())
            {
                RemoveRange(oldResults);
                await SaveAsync();
                return true;
            }
            return false;
        }
    }

    public interface ICollectionLogRepository : IRepository<CollectionLog>
    {
        Task<IEnumerable<CollectionLog>> GetLogsByLevelAsync(LogLevel logLevel);
        Task<IEnumerable<CollectionLog>> GetLogsByDeviceIdAsync(int deviceId);
        Task<IEnumerable<CollectionLog>> GetLogsByDateRangeAsync(DateTime startDate, DateTime endDate);
        Task<IEnumerable<CollectionLog>> GetErrorLogsAsync();
        Task<int> CountLogsByLevelAsync(LogLevel logLevel);
        Task<bool> DeleteOldLogsAsync(int keepDays = 90);
    }

    public class CollectionLogRepository : Repository<CollectionLog>, ICollectionLogRepository
    {
        private readonly CNCDbContext _context;

        public CollectionLogRepository(CNCDbContext context) : base(context)
        {
            _context = context;
        }

        public async Task<IEnumerable<CollectionLog>> GetLogsByLevelAsync(LogLevel logLevel)
        {
            return await _context.CollectionLogs
                .Where(cl => cl.LogLevel == logLevel)
                .OrderByDescending(cl => cl.LogTime)
                .ToListAsync();
        }

        public async Task<IEnumerable<CollectionLog>> GetLogsByDeviceIdAsync(int deviceId)
        {
            return await _context.CollectionLogs
                .Where(cl => cl.DeviceId == deviceId)
                .OrderByDescending(cl => cl.LogTime)
                .ToListAsync();
        }

        public async Task<IEnumerable<CollectionLog>> GetLogsByDateRangeAsync(DateTime startDate, DateTime endDate)
        {
            return await _context.CollectionLogs
                .Where(cl => cl.LogTime >= startDate && cl.LogTime <= endDate)
                .OrderByDescending(cl => cl.LogTime)
                .ToListAsync();
        }

        public async Task<IEnumerable<CollectionLog>> GetErrorLogsAsync()
        {
            return await _context.CollectionLogs
                .Where(cl => cl.LogLevel == LogLevel.Error || cl.LogLevel == LogLevel.Critical)
                .OrderByDescending(cl => cl.LogTime)
                .ToListAsync();
        }

        public async Task<int> CountLogsByLevelAsync(LogLevel logLevel)
        {
            return await _context.CollectionLogs
                .CountAsync(cl => cl.LogLevel == logLevel);
        }

        public async Task<bool> DeleteOldLogsAsync(int keepDays = 90)
        {
            var cutoffDate = DateTime.Now.AddDays(-keepDays);
            var oldLogs = await _context.CollectionLogs
                .Where(cl => cl.LogTime < cutoffDate)
                .ToListAsync();

            if (oldLogs.Any())
            {
                RemoveRange(oldLogs);
                await SaveAsync();
                return true;
            }
            return false;
        }
    }
}