using Hc900Crawler.Core.Application.DTOs;
using Hc900Crawler.Core.Domain.Entities;

namespace Hc900Crawler.Core.Application.Interfaces;

// ── Database ─────────────────────────────────────────────────────────────────

public interface IExperionDbService
{
    Task<bool> InitializeAsync();
    
    // ── TimeScaleDB 하이퍼테이블 ──────────────────────────────────────────────────
    Task<Hc900Crawler.Infrastructure.Database.HypertableStatusInfo> GetHypertableStatusAsync();
    Task<Hc900Crawler.Infrastructure.Database.HypertableCreateResult> CreateHypertableAsync(
        Hc900Crawler.Infrastructure.Database.HypertableCreateRequest request);

    // ── RealtimeTable ─────────────────────────────────────────────────────────
    Task<int>  BuildRealtimeTableAsync(IEnumerable<PointBuilderGroupDto> groups);
    Task<PointBuilderPreviewResult> PreviewRealtimeBuildAsync(IEnumerable<(string GroupKey, PointBuilderGroupDto Group)> groups);
    Task<int> ApplySelectedPointsAsync(IEnumerable<string> selectedNodeIds);
    Task<int> AppendPointsAsync(IEnumerable<string> nodeIds);
    Task<IEnumerable<RealtimePoint>> GetRealtimePointsAsync();
    Task<RealtimePoint> AddRealtimePointAsync(string nodeId);
    /// <summary>포인트(realtime_table 행) 삭제. 같은 base_tag의 잔여 행이 0이면 고아 tag_metadata(desc/area/sub_area)도 정리.
    /// purgeHistory=true면 해당 tagname의 history_table 이력까지 영구 삭제(복구 불가, 명시적 opt-in).</summary>
    Task<PointDeleteResult> DeleteRealtimePointAsync(int id, bool purgeHistory = false);
    Task<int>  UpdateLiveValueAsync(string nodeId, string? value, DateTime timestamp);
    Task<int>  BatchUpdateLiveValuesAsync(IEnumerable<LiveValueUpdate> updates);

    // ── HistoryTable ──────────────────────────────────────────────────────────
    Task<int> SnapshotToHistoryAsync(string? controllerId = null, bool includeDigital = false);
    Task<IEnumerable<string>> GetTagNamesAsync();
    Task<HistoryQueryResult>  QueryHistoryAsync(
        IEnumerable<string> tagNames, DateTime? from, DateTime? to, int limit);
    
    /// <summary>
    /// 사용자 지정 간격으로 history 이력 조회
    /// history_table의 기본 저장 간격(60초)을 기반으로 사용자가 요청한 간격으로 데이터 집계
    /// </summary>
    /// <param name="request">조회 요청 (태그명, 시간 범위, 간격 등)</param>
    /// <returns>집계된 이력 데이터</returns>
    Task<HistoryIntervalQueryResult> QueryHistoryWithIntervalAsync(HistoryIntervalQueryRequest request);

    // ── FastSession ───────────────────────────────────────────────────────────────
    Task<FastSession>              CreateFastSessionAsync(FastSessionCreateRequest request);
    Task                           UpdateFastSessionStatusAsync(int sessionId, string status);
    Task                           UpdateFastSessionRowCountAsync(int sessionId, int rowCount);
    Task                           UpdateFastSessionPinnedAsync(int sessionId, bool pinned);
    Task<FastSession?>             GetFastSessionAsync(int sessionId);
    Task<IEnumerable<FastSession>> GetFastSessionsAsync();
    Task                           DeleteFastSessionAsync(int sessionId);
    Task<IEnumerable<FastSession>> GetExpiredFastSessionsAsync();

    // ── FastRecord ────────────────────────────────────────────────────────────────
    Task<FastQueryResult> GetFastRecordsAsync(int sessionId, DateTime? from, DateTime? to);
    Task                  BatchInsertFastRecordsAsync(IEnumerable<FastRecord> records);
    Task                  ExportFastRecordsToCsvAsync(int sessionId, Stream stream, DateTime? from, DateTime? to);
    
    // ── Realtime → Fast 복사용 ────────────────────────────────────────────────────
    /// <summary>realtime_table에서 태그명 목록으로 livevalue와 timestamp 가져오기</summary>
    Task<IEnumerable<RealtimePoint>> GetRealtimeRecordsByTagNamesAsync(IEnumerable<string> tagNames);

    // ── 공통 (이미 없는 경우만) ──────────────────────────────────────────────────
    Task<string?> GetNodeIdByTagNameAsync(string tagName);

    // ── Digital Event History ──────────────────────────────────────────────────
    /// <summary>디지털 태그 이름 목록 조회 (value 패턴 또는 tag_metadata 기반)</summary>
    Task<IEnumerable<string>> GetDigitalTagNamesAsync();

    /// <summary>디지털 포인트 현재 값 조회</summary>
    Task<IEnumerable<RealtimePoint>> GetDigitalPointsAsync();

    /// <summary>디지털 이벤트 기록</summary>
    Task<int> RecordDigitalEventAsync(DigitalEventRecord record);

    /// <summary>디지털 이벤트 배치 기록</summary>
    Task<int> BatchRecordDigitalEventsAsync(IEnumerable<DigitalEventRecord> records);

    /// <summary>태그명으로 area 조회 (tag_metadata 기반)</summary>
    Task<string?> GetAreaByTagNameAsync(string tagName);

    // ── Sub-Area (세부 Area: P6-1/P6-2 등) ──────────────────────────────────────
    /// <summary>태그명으로 sub_area 조회 (tag_metadata attribute='sub_area'). 공용이면 "P6-1,P6-2" 형식</summary>
    Task<string?> GetSubAreaByTagNameAsync(string tagName);

    /// <summary>area 코드("P6") 또는 sub_area 코드("P6-1")별 태그 목록 + sub_area 현황 (페이지네이션)</summary>
    Task<(List<SubAreaTagDto> tags, int total)> GetSubAreaListByAreaAsync(string area, int page, int pageSize);

    /// <summary>단일 태그 sub_area 수정/삭제 (subArea=null이면 삭제=미분류). "P2-1,P2-2"로 공용 지정 가능</summary>
    Task<bool> UpdateSubAreaAsync(string baseTag, string? subArea);

    /// <summary>area-scoped 번호 prefix 규칙 + pid_equipment 공용 검출로 sub_area 일괄 seed</summary>
    Task<SubAreaSeedResultDto> SeedSubAreaAsync(bool dryRun);

    /// <summary>이벤트 히스토리 조회</summary>
    Task<IEnumerable<EventHistoryRow>> QueryEventHistoryAsync(
        string? tagName, string? area, string? subArea,
        string? eventType, DateTime from, DateTime to, int limit = 500);
}

// ── Result records ───────────────────────────────────────────────────────────

// DTO는 record 타입으로 유지. DisplayName null 방지는 Infrastructure 레이어에서 처리

// DisplayName 필드는 null 방지를 위해 대체 기본값이 필요하지만,
// record는 null 불가 필드를 가질 수 없으므로 Infrastructure 레이어에서 null 대체 처리
public record NodeMapStats(int Total, int ObjectCount, int VariableCount, int MaxLevel, IEnumerable<string> DataTypes);
public record NodeMapQueryResult(int Total, IEnumerable<object> Items);
public record HistoryQueryResult(IEnumerable<string> TagNames, IEnumerable<HistoryRow> Rows);
public record HistoryRow(DateTime RecordedAt, IReadOnlyDictionary<string, string?> Values);

// ── History Interval Query ─────────────────────────────────────────────────────
public record HistoryIntervalQueryRequest(
    IEnumerable<string> TagNames,
    DateTime? From,
    DateTime? To,
    string Interval,
    int Limit);

public record HistoryIntervalQueryResult(
    IEnumerable<string> TagNames,
    IEnumerable<HistoryIntervalRow> Rows,
    int BaseIntervalSeconds,
    string QueryInterval);

public record HistoryIntervalRow(DateTime TimeBucket, IReadOnlyDictionary<string, string?> Values);

public record LiveValueUpdate(string NodeId, string? Value, DateTime Timestamp);

// ── fastTable DTOs ────────────────────────────────────────────────────────────

public record FastSessionInfo(
    int      Id,
    string   Name,
    DateTime StartedAt,
    DateTime? EndedAt,
    string   Status,
    int      SamplingMs,
    int      DurationSec,
    string[] TagList,
    int      RowCount,
    int?     RetentionDays,
    bool     Pinned
);

public record FastSessionStartRequest(
    string   Name,
    int      SamplingMs,
    int      DurationSec,
    string[] TagList,
    int?     RetentionDays = null
);

public record FastSessionCreateRequest(
    string   Name,
    int      SamplingMs,
    int      DurationSec,
    string[] TagList,
    int?     RetentionDays = null
);

public record FastQueryResult(
    int                    SessionId,
    DateTime               From,
    DateTime               To,
    string[]               TagNames,
    IEnumerable<FastRecord> Items,
    int                    TotalCount
);

public record PinRequest(bool Pinned);

// ── P&ID Extractor ─────────────────────────────────────────────────────────────

public interface IPidExtractorService
{
    // 추출
    Task<PidExtractionResult> ExtractFromFileAsync(string filePath, bool useImageMode = false);
    Task<PidExtractionResult> ExtractFromStreamAsync(Stream stream, string fileName, bool useImageMode = false);
    
    // 조회 (페이지네이션)
    Task<(int Total, IEnumerable<PidEquipment> Items)> GetEquipmentAsync(
        string? tagNo, string? category, int page, int pageSize,
        string? sortBy = null, bool sortDesc = false);
    
    Task<PidEquipment?> GetByIdAsync(long id);
    
    // 업데이트
    Task UpdateConfidenceAsync(long id, double confidence);
    Task ActivateAsync(long id);
    Task DeactivateAsync(long id);
    Task<PidEquipment> CreateEquipmentAsync(CreateEquipmentRequest request);
    Task<bool> UpdateEquipmentAsync(long id, UpdateEquipmentRequest request);
    Task<(bool Found, string? TagName)> AutoMapTagAsync(long id);
    Task<bool> DeleteAsync(long id);
    
    // 통계
    Task<int> GetTotalCountAsync();
    Task<int> GetConfidenceItemsCountAsync();
    Task<int> GetLowConfidenceItemsCountAsync();
    Task<IDictionary<string, int>> GetConfidenceDistributionAsync();
    Task<int> GetDrawingCountAsync();
    
    // 내보내기 / 가져오기
    Task<string> ExportToCsvAsync(IEnumerable<PidEquipment> items);
    Task<byte[]> ExportToExcelAsync(IEnumerable<PidEquipment> items);
    Task<PidImportResult> ImportFromExcelAsync(Stream stream);

    // Prefix 규칙
    Task<List<PidPrefixRule>> GetPrefixRulesAsync();
    Task<PidPrefixRule> CreatePrefixRuleAsync(CreatePidPrefixRuleRequest request);
    Task<PidPrefixRule?> UpdatePrefixRuleAsync(int id, UpdatePidPrefixRuleRequest request);
    Task<bool> DeletePrefixRuleAsync(int id);

    // 카테고리 적용
    Task<int> ApplyCategoriesToExistingAsync();

    // 연결 분석 (from→to)
    Task<int> AnalyzeConnectionsAsync(string drawingFile);

    // 전체 삭제
    Task<int> DeleteAllEquipmentAsync();
}

// ── P&ID Tag Mapping ───────────────────────────────────────────────────────────

public interface ITagMappingService
{
    Task<(int Total, IEnumerable<TagMappingResult> Items)> GetMappingsAsync(int page, int pageSize);
    Task<TagMappingResult?> GetMappingByIdAsync(long id);
    Task<TagMappingResult> CreateMappingAsync(CreateMappingRequest request);
    Task UpdateMappingAsync(long id, UpdateMappingRequest request);
    Task ClearMappingAsync(long id);
    
    Task<int> GetUnmappedCountAsync();
    Task<int> GetMappedCountAsync();
    Task<IEnumerable<string>> GetAvailableTagsAsync();
}

// ── HC900 Gateway ─────────────────────────────────────────────────────────────

public interface IHc900GatewayService
{
    Task<Hc900.Gateway.HealthCheckResponse> GetHealthAsync();
    Task<Hc900.Gateway.ListTagsResponse> ListTagsAsync(string filter = "", int limit = 0);
    Task<Hc900.Gateway.WriteTagResponse> WriteTagAsync(string tagName, double value);
    bool IsConnected { get; }
    long LastPollCount { get; }
}

// ── HC900 Realtime ────────────────────────────────────────────────────────────

public interface IHc900RealtimeService
{
    bool IsConnected { get; }
    long PollCount { get; }
    DateTime? LastPollAt { get; }
    IReadOnlyDictionary<string, bool> ControllerConnected { get; }
}

// ── Digital Event ─────────────────────────────────────────────────────────────

public class DigitalEventRecord
{
    public string ControllerId { get; set; } = "HC1";
    public string TagName { get; set; } = "";
    public string NodeId { get; set; } = "";
    public string? PrevValue { get; set; }
    public string CurrValue { get; set; } = "";
    public string EventType { get; set; } = "";
    public DateTime EventTime { get; set; }
    public int? DurationSeconds { get; set; }
    public string? Area { get; set; }
    public string? SubArea { get; set; }
    public string? Metadata { get; set; }
}

public class EventHistoryRow
{
    public long Id { get; set; }
    public string TagName { get; set; } = "";
    public string NodeId { get; set; } = "";
    public string? PrevValue { get; set; }
    public string CurrValue { get; set; } = "";
    public string EventType { get; set; } = "";
    public DateTime EventTime { get; set; }
    public string? Area { get; set; }
    public string? SubArea { get; set; }
    public int? DurationSeconds { get; set; }
    public string? Metadata { get; set; }
}
