package web

import (
	"testing"

	"axenov/iptv-checker/app/playlist"
)

func TestHasTokensString(t *testing.T) {
	cases := []struct {
		in   string
		want bool
	}{
		{"http://example.com/stream.m3u8?token=abc", true},
		{"http://example.com/stream?user=test&pwd=secret", true},
		{"http://example.com/stream?drmreq=xyz", true},
		{"http://example.com/stream?u=foo", true},
		{"http://example.com/stream?password=123", true},
		{"http://example.com/stream.m3u8", false},
		{"", false},
		{"plain text without tokens", false},
	}
	for _, c := range cases {
		got := hasTokensString(c.in)
		if got != c.want {
			t.Errorf("hasTokensString(%q) = %v, want %v", c.in, got, c.want)
		}
	}
}

func TestStatusClass(t *testing.T) {
	cases := []struct {
		view *PlaylistView
		want string
	}{
		{&PlaylistView{IsKnown: false}, "secondary"},
		{&PlaylistView{IsKnown: true, Playlist: playlist.Playlist{IsOnline: true}}, "success"},
		{&PlaylistView{IsKnown: true, Playlist: playlist.Playlist{IsOnline: false}}, "danger"},
	}
	for _, c := range cases {
		got := statusClass(c.view)
		if got != c.want {
			t.Errorf("statusClass() = %s, want %s", got, c.want)
		}
	}
}

func TestStatusBadge(t *testing.T) {
	cases := []struct {
		view *PlaylistView
		want string
	}{
		{&PlaylistView{IsKnown: false}, "unknown"},
		{&PlaylistView{IsKnown: true, Playlist: playlist.Playlist{IsOnline: true}}, "online"},
		{&PlaylistView{IsKnown: true, Playlist: playlist.Playlist{IsOnline: false}}, "offline"},
	}
	for _, c := range cases {
		got := statusBadge(c.view)
		if got != c.want {
			t.Errorf("statusBadge() = %s, want %s", got, c.want)
		}
	}
}

func TestFormatDate(t *testing.T) {
	if formatDate(0) != "" {
		t.Error("expected empty string for ts=0")
	}
	result := formatDate(1700000000)
	if result == "" {
		t.Error("expected non-empty date string")
	}
}

func TestIsAlphanumeric(t *testing.T) {
	cases := []struct {
		in   string
		want bool
	}{
		{"ABC123", true},
		{"abc", true},
		{"123", true},
		{"", false},
		{"ABC-123", false},
		{"ABC_123", false},
		{"ABC 123", false},
		{"Привет", false},
	}
	for _, c := range cases {
		got := isAlphanumeric(c.in)
		if got != c.want {
			t.Errorf("isAlphanumeric(%q) = %v, want %v", c.in, got, c.want)
		}
	}
}

func TestParseCacheVersion(t *testing.T) {
	info := "# Server\nredis_version:6.2.6\nredis_mode:standalone\n"
	got := parseCacheVersion(info)
	if got != "6.2.6" {
		t.Errorf("expected '6.2.6', got '%s'", got)
	}
}

func TestParseCacheVersion_NotFound(t *testing.T) {
	info := "# Server\nsome_other_field:foo\n"
	got := parseCacheVersion(info)
	if got != "unknown" {
		t.Errorf("expected 'unknown', got '%s'", got)
	}
}

func TestContainsTag(t *testing.T) {
	tags := []string{"hd", "adult", "russian"}
	if !containsTag(tags, "adult") {
		t.Error("expected true for 'adult'")
	}
	if containsTag(tags, "movies") {
		t.Error("expected false for 'movies'")
	}
	if containsTag([]string{}, "x") {
		t.Error("expected false for empty list")
	}
}

func TestMakeUnknownView(t *testing.T) {
	iniPls := playlist.Playlist{
		Name:        "Test",
		Description: "Desc",
		Url:         "http://example.com",
		Source:      "public",
	}
	view := makeUnknownView("CODE", iniPls)
	if view.Code != "CODE" {
		t.Errorf("expected Code='CODE', got '%s'", view.Code)
	}
	if view.IsKnown {
		t.Error("expected IsKnown=false")
	}
	if view.Name != "Test" {
		t.Errorf("expected Name='Test', got '%s'", view.Name)
	}
	if len(view.Tags) != 0 {
		t.Errorf("expected 0 tags, got %d", len(view.Tags))
	}
}

func TestEnrichPlaylist_OnlinePercent(t *testing.T) {
	view := &PlaylistView{
		Playlist: playlist.Playlist{
			IsOnline:     true,
			OnlineCount:  8,
			OfflineCount: 2,
			Channels: map[string]playlist.Channel{
				"1":  {},
				"2":  {},
				"3":  {},
				"4":  {},
				"5":  {},
				"6":  {},
				"7":  {},
				"8":  {},
				"9":  {},
				"10": {},
			},
			Attributes: map[string]string{},
			Groups:     map[string]playlist.Group{},
			Content:    "#EXTM3U",
		},
		IsKnown: true,
	}
	enrichPlaylist(view)
	if view.OnlinePercent != 80 {
		t.Errorf("expected 80%%, got %d%%", view.OnlinePercent)
	}
	if view.OfflinePercent != 20 {
		t.Errorf("expected 20%%, got %d%%", view.OfflinePercent)
	}
}

func TestEnrichPlaylist_Unknown(t *testing.T) {
	view := &PlaylistView{
		Playlist: playlist.Playlist{
			Channels:   map[string]playlist.Channel{"1": {}},
			Attributes: map[string]string{},
			Groups:     map[string]playlist.Group{},
			Content:    "",
		},
		IsKnown: false,
	}
	enrichPlaylist(view)
	if view.OnlinePercent != 0 || view.OfflinePercent != 0 {
		t.Errorf("expected 0%% for unknown, got online=%d%% offline=%d%%",
			view.OnlinePercent, view.OfflinePercent)
	}
}

func TestEnrichPlaylist_HasCatchup(t *testing.T) {
	view := &PlaylistView{
		Playlist: playlist.Playlist{
			Content:    "#EXTM3U catchup=default",
			Channels:   map[string]playlist.Channel{},
			Attributes: map[string]string{},
			Groups:     map[string]playlist.Group{},
		},
		IsKnown: true,
	}
	enrichPlaylist(view)
	if !view.HasCatchup {
		t.Error("expected HasCatchup=true")
	}
}

func TestEnrichPlaylist_HasTvg(t *testing.T) {
	view := &PlaylistView{
		Playlist: playlist.Playlist{
			Attributes: map[string]string{"url-tvg": "http://example.com/epg.xml"},
			Channels:   map[string]playlist.Channel{},
			Groups:     map[string]playlist.Group{},
			Content:    "",
		},
		IsKnown: true,
	}
	enrichPlaylist(view)
	if !view.HasTvg {
		t.Error("expected HasTvg=true")
	}
}

func TestEnrichPlaylist_HasTokens(t *testing.T) {
	view := &PlaylistView{
		Playlist: playlist.Playlist{
			Url:        "http://example.com/list.m3u8?token=abc",
			Content:    "#EXTM3U",
			Channels:   map[string]playlist.Channel{},
			Attributes: map[string]string{},
			Groups:     map[string]playlist.Group{},
		},
		IsKnown: true,
	}
	enrichPlaylist(view)
	if !view.HasTokens {
		t.Error("expected HasTokens=true")
	}
}

func TestEnrichPlaylist_TagsCollection(t *testing.T) {
	view := &PlaylistView{
		Playlist: playlist.Playlist{
			Channels: map[string]playlist.Channel{
				"1": {Tags: []string{"hd", "russian"}},
				"2": {Tags: []string{"adult", "hd"}},
			},
			Attributes: map[string]string{},
			Groups:     map[string]playlist.Group{},
			Content:    "",
		},
		IsKnown: true,
	}
	enrichPlaylist(view)
	if len(view.Tags) != 3 {
		t.Errorf("expected 3 unique tags, got %d: %v", len(view.Tags), view.Tags)
	}
}

func TestEnrichPlaylist_ChannelHasToken(t *testing.T) {
	view := &PlaylistView{
		Playlist: playlist.Playlist{
			Channels: map[string]playlist.Channel{
				"1": {URL: "http://example.com/stream?token=abc"},
			},
			Attributes: map[string]string{},
			Groups:     map[string]playlist.Group{},
			Content:    "",
		},
		IsKnown: true,
	}
	enrichPlaylist(view)
	for _, cv := range view.ViewChannels {
		if !cv.HasToken {
			t.Error("expected channel HasToken=true")
		}
	}
}

func TestComputeStats(t *testing.T) {
	playlists := []PlaylistView{
		{
			Playlist: playlist.Playlist{
				Code:       "RU",
				IsOnline:   true,
				CheckedAt:  1700000000,
				Channels:   map[string]playlist.Channel{"1": {IsOnline: true}, "2": {IsOnline: false}},
				Groups:     map[string]playlist.Group{"g1": {}},
				Attributes: map[string]string{"url-tvg": "http://epg.xml"},
				Content:    "#EXTM3U catchup=default",
			},
			IsKnown:    true,
			HasCatchup: true,
			HasTvg:     true,
			Tags:       []string{"adult"},
		},
		{
			Playlist: playlist.Playlist{
				Code:      "EN",
				IsOnline:  false,
				CheckedAt: 1700000100,
				Channels:  map[string]playlist.Channel{"1": {IsOnline: false}},
			},
			IsKnown: false,
		},
	}
	stats := computeStats(playlists)
	plsStats := stats["playlists"].(map[string]interface{})
	if plsStats["all"].(int) != 2 {
		t.Errorf("expected 2 playlists, got %d", plsStats["all"])
	}
	if plsStats["online"].(int) != 1 {
		t.Errorf("expected 1 online, got %d", plsStats["online"])
	}
	if plsStats["unknown"].(int) != 1 {
		t.Errorf("expected 1 unknown, got %d", plsStats["unknown"])
	}
	if plsStats["adult"].(int) != 1 {
		t.Errorf("expected 1 adult, got %d", plsStats["adult"])
	}
	if plsStats["hasCatchup"].(int) != 1 {
		t.Errorf("expected 1 catchup, got %d", plsStats["hasCatchup"])
	}
	if plsStats["hasTvg"].(int) != 1 {
		t.Errorf("expected 1 tvg, got %d", plsStats["hasTvg"])
	}
	if plsStats["groupped"].(int) != 1 {
		t.Errorf("expected 1 groupped, got %d", plsStats["groupped"])
	}

	chStats := stats["channels"].(map[string]interface{})
	if chStats["all"].(int) != 3 {
		t.Errorf("expected 3 channels, got %d", chStats["all"])
	}
	if chStats["online"].(int) != 1 {
		t.Errorf("expected 1 online channel, got %d", chStats["online"])
	}
	if chStats["offline"].(int) != 2 {
		t.Errorf("expected 2 offline channels, got %d", chStats["offline"])
	}

	latest := plsStats["latest"].(map[string]interface{})
	if latest["code"] != "EN" {
		t.Errorf("expected latest code 'EN', got '%v'", latest["code"])
	}
}

func TestComputeStats_Empty(t *testing.T) {
	stats := computeStats([]PlaylistView{})
	plsStats := stats["playlists"].(map[string]interface{})
	if plsStats["all"].(int) != 0 {
		t.Errorf("expected 0, got %d", plsStats["all"])
	}
}
