package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"strings"
	"time"

	"gopkg.in/yaml.v2"

	"github.com/matrix-org/gomatrix"
	"github.com/mmcdole/gofeed"
	"golang.org/x/net/html"
)

type Config struct {
	Homeserver string `yaml:"homeserver"`
	Username   string `yaml:"username"`
	Password   string `yaml:"password"`
	Feeds      []struct {
		URL   string `yaml:"url"`
		Alias string `yaml:"alias"`
	} `yaml:"feeds"`
}

var (
	client        *gomatrix.Client
	processedFeed map[string]map[string]bool // 保存已处理的条目链接
)

func loadConfig(filename string) (*Config, error) {
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	var config Config
	err = yaml.Unmarshal(data, &config)
	if err != nil {
		return nil, err
	}
	return &config, nil
}

func login(config *Config) error {
	var err error
	client, err = gomatrix.NewClient(config.Homeserver, "", "")
	if err != nil {
		return err
	}
	resp, err := client.Login(&gomatrix.ReqLogin{
		Type:     "m.login.password",
		User:     config.Username,
		Password: config.Password,
	})
	if err != nil {
		return err
	}
	client.SetCredentials(resp.UserID, resp.AccessToken)
	return nil
}

func processFeed(fp *gofeed.Parser, feedURL, alias string) error {
	rssFeed, err := fp.ParseURL(feedURL)
	if err != nil {
		return fmt.Errorf("failed to parse feed %s: %v", feedURL, err)
	}

	if processedFeed[alias] == nil {
		processedFeed[alias] = make(map[string]bool)
	}

	for _, item := range rssFeed.Items {
		if processedFeed[alias][item.Link] {
			continue // Skip already processed items
		}

		// Extract and format message content
		message := formatMessage(item)
		htmlMessage := formatHTMLMessage(item)

		_, err := client.SendMessageEvent(alias, "m.room.message", map[string]interface{}{
			"msgtype":        "m.text",
			"body":           message,
			"format":         "org.matrix.custom.html",
			"formatted_body": htmlMessage,
		})
		if err != nil {
			log.Printf("Failed to send message to %s: %v", alias, err)
		} else {
			log.Printf("Successfully sent message to %s", alias)
			processedFeed[alias][item.Link] = true
		}
	}
	return nil
}

func formatMessage(item *gofeed.Item) string {
	message := fmt.Sprintf("**%s**\n%s\n\n%s", item.Title, item.Published, item.Link)
	if item.Description != "" {
		message += fmt.Sprintf("\n\n%s", extractText(item.Description))
	}
	if item.Image != nil {
		message += fmt.Sprintf("\n\n![Image](%s)", item.Image.URL)
	}
	if item.Author != nil {
		message += fmt.Sprintf("\n\nAuthor: %s", item.Author.Name)
	}
	message += "\n\nSponsor: 更流畅的富强节点，与时代共「加速」"
	return message
}

func formatHTMLMessage(item *gofeed.Item) string {
	htmlMessage := fmt.Sprintf("<strong>%s</strong><br>%s<br><a href=\"%s\">%s</a><br><br>%s",
		item.Title, item.Published, item.Link, item.Link, extractText(item.Description))
	if item.Image != nil {
		htmlMessage += fmt.Sprintf("<br><img src=\"%s\" alt=\"Image\">", item.Image.URL)
	}
	if item.Author != nil {
		htmlMessage += fmt.Sprintf("<br><br>Author: %s", item.Author.Name)
	}
	htmlMessage += "<br><br>Sponsor: 更流畅的富强节点，与时代共「加速」"
	return htmlMessage
}

// Extract text content from HTML
func extractText(htmlContent string) string {
	doc, err := html.Parse(strings.NewReader(htmlContent))
	if err != nil {
		log.Printf("Failed to parse HTML content: %v", err)
		return ""
	}
	var textContent strings.Builder
	var f func(*html.Node)
	f = func(n *html.Node) {
		if n.Type == html.TextNode {
			textContent.WriteString(n.Data)
		}
		for c := n.FirstChild; c != nil; c = c.NextSibling {
			f(c)
		}
	}
	f(doc)
	return textContent.String()
}

func main() {
	config, err := loadConfig("config.yaml")
	if err != nil {
		log.Fatalf("Failed to load config: %v", err)
	}

	err = login(config)
	if err != nil {
		log.Fatalf("Failed to login: %v", err)
	}

	fp := gofeed.NewParser()
	processedFeed = make(map[string]map[string]bool)

	ticker := time.NewTicker(10 * time.Minute)
	defer ticker.Stop()

	for {
		for _, feed := range config.Feeds {
			err := processFeed(fp, feed.URL, feed.Alias)
			if err != nil {
				log.Printf("Error processing feed %s: %v", feed.URL, err)
			}
		}
		<-ticker.C
	}
}
