using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
using Tornado3_2026Election.Common;

namespace Tornado3_2026Election.Domain;

public sealed class CandidateEntry : ObservableObject
{
    private string _candidateCode = string.Empty;
    private string _ballotNumber = string.Empty;
    private string _name = string.Empty;
    private string _party = string.Empty;
    private string _colorParty = string.Empty;
    private int _voteCount;
    private double _voteRate;
    private bool _hasImage = true;
    private CandidateJudgement _manualJudgement;
    private CandidateJudgement _automaticJudgement;
    private string _careerPromise1 = string.Empty;
    private string _careerPromise2 = string.Empty;
    private string _careerPromise3 = string.Empty;

    public Guid Id { get; set; } = Guid.NewGuid();

    public string CandidateCode
    {
        get => _candidateCode;
        set => SetProperty(ref _candidateCode, value);
    }

    public string BallotNumber
    {
        get => _ballotNumber;
        set => SetProperty(ref _ballotNumber, value ?? string.Empty);
    }

    public string Name
    {
        get => _name;
        set => SetProperty(ref _name, value);
    }

    public string Party
    {
        get => _party;
        set => SetProperty(ref _party, value);
    }

    public string ColorParty
    {
        get => _colorParty;
        set => SetProperty(ref _colorParty, value);
    }

    [JsonIgnore]
    public string BroadcastDistrictName { get; set; } = string.Empty;

    [JsonIgnore]
    public string BroadcastRegionName { get; set; } = string.Empty;

    [JsonIgnore]
    public string BroadcastElectionDistrictName { get; set; } = string.Empty;

    [JsonIgnore]
    public string BroadcastDistrictCode { get; set; } = string.Empty;

    [JsonIgnore]
    public double? BroadcastCountedRate { get; set; }

    [JsonIgnore]
    public int BroadcastRank { get; set; }

    [JsonIgnore]
    public int BroadcastSeatCount { get; set; }

    [JsonIgnore]
    public bool BroadcastCountingClosed { get; set; }

    [JsonIgnore]
    public bool IsWithinBroadcastSeatCount =>
        BroadcastRank > 0 &&
        BroadcastSeatCount > 0 &&
        BroadcastRank <= BroadcastSeatCount;

    public int VoteCount
    {
        get => _voteCount;
        set => SetProperty(ref _voteCount, value);
    }

    public double VoteRate
    {
        get => _voteRate;
        set => SetProperty(ref _voteRate, value);
    }

    public bool HasImage
    {
        get => _hasImage;
        set
        {
            if (SetProperty(ref _hasImage, value))
            {
                OnPropertyChanged(nameof(HasImageLabel));
            }
        }
    }

    public CandidateJudgement ManualJudgement
    {
        get => _manualJudgement;
        set
        {
            if (SetProperty(ref _manualJudgement, value))
            {
                OnPropertyChanged(nameof(EffectiveJudgement));
                OnPropertyChanged(nameof(EffectiveJudgementLabel));
            }
        }
    }

    public CandidateJudgement AutomaticJudgement
    {
        get => _automaticJudgement;
        set
        {
            if (SetProperty(ref _automaticJudgement, value))
            {
                OnPropertyChanged(nameof(EffectiveJudgement));
                OnPropertyChanged(nameof(EffectiveJudgementLabel));
            }
        }
    }

    public string CareerPromise1
    {
        get => _careerPromise1;
        set
        {
            if (SetProperty(ref _careerPromise1, value ?? string.Empty))
            {
                OnPropertyChanged(nameof(CareerPromises), nameof(HasCareerPromises));
            }
        }
    }

    public string CareerPromise2
    {
        get => _careerPromise2;
        set
        {
            if (SetProperty(ref _careerPromise2, value ?? string.Empty))
            {
                OnPropertyChanged(nameof(CareerPromises), nameof(HasCareerPromises));
            }
        }
    }

    public string CareerPromise3
    {
        get => _careerPromise3;
        set
        {
            if (SetProperty(ref _careerPromise3, value ?? string.Empty))
            {
                OnPropertyChanged(nameof(CareerPromises), nameof(HasCareerPromises));
            }
        }
    }

    [JsonIgnore]
    public CandidateJudgement EffectiveJudgement => ManualJudgement != CandidateJudgement.None ? ManualJudgement : AutomaticJudgement;

    [JsonIgnore]
    public string EffectiveJudgementLabel => EffectiveJudgement switch
    {
        CandidateJudgement.Leading => "유력",
        CandidateJudgement.Confirmed => "확정",
        CandidateJudgement.Elected => "당선",
        CandidateJudgement.ElectedInProgress => "개표중 당선",
        CandidateJudgement.UnopposedElected => "무투표 당선",
        CandidateJudgement.ElectedAfterCountComplete => "개표마감 당선",
        _ => "-"
    };

    [JsonIgnore]
    public string VoteCountDisplay => VoteCount.ToString("N0");

    [JsonIgnore]
    public string EffectiveColorParty => string.IsNullOrWhiteSpace(ColorParty) ? Party : ColorParty;

    [JsonIgnore]
    public bool HasCareerPromises => CareerPromises.Length > 0;

    [JsonIgnore]
    public string[] CareerPromises => new[]
    {
        CareerPromise1?.Trim() ?? string.Empty,
        CareerPromise2?.Trim() ?? string.Empty,
        CareerPromise3?.Trim() ?? string.Empty
    }
        .Where(promise => !string.IsNullOrWhiteSpace(promise))
        .ToArray();

    [JsonIgnore]
    public string VoteRateDisplay => Math.Round(VoteRate, 1, MidpointRounding.AwayFromZero).ToString("0.0");

    [JsonIgnore]
    public string HasImageLabel => HasImage ? "있음" : "없음";

    public void ApplyCareerPromises(IReadOnlyList<string>? promises)
    {
        CareerPromise1 = promises is not null && promises.Count > 0 ? promises[0] ?? string.Empty : string.Empty;
        CareerPromise2 = promises is not null && promises.Count > 1 ? promises[1] ?? string.Empty : string.Empty;
        CareerPromise3 = promises is not null && promises.Count > 2 ? promises[2] ?? string.Empty : string.Empty;
    }

    public CandidateEntry Clone()
    {
        return new CandidateEntry
        {
            Id = Id,
            CandidateCode = CandidateCode,
            BallotNumber = BallotNumber,
            Name = Name,
            Party = Party,
            ColorParty = ColorParty,
            BroadcastDistrictName = BroadcastDistrictName,
            BroadcastRegionName = BroadcastRegionName,
            BroadcastElectionDistrictName = BroadcastElectionDistrictName,
            BroadcastDistrictCode = BroadcastDistrictCode,
            BroadcastCountedRate = BroadcastCountedRate,
            BroadcastRank = BroadcastRank,
            BroadcastSeatCount = BroadcastSeatCount,
            BroadcastCountingClosed = BroadcastCountingClosed,
            VoteCount = VoteCount,
            VoteRate = VoteRate,
            HasImage = HasImage,
            ManualJudgement = ManualJudgement,
            AutomaticJudgement = AutomaticJudgement,
            CareerPromise1 = CareerPromise1,
            CareerPromise2 = CareerPromise2,
            CareerPromise3 = CareerPromise3
        };
    }
}
