// Copyright 2019 Frédéric Guillot. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

package atom // import "miniflux.app/reader/atom"

import (
	"encoding/base64"
	"html"
	"strings"
	"time"

	"miniflux.app/crypto"
	"miniflux.app/logger"
	"miniflux.app/model"
	"miniflux.app/reader/date"
	"miniflux.app/reader/sanitizer"
	"miniflux.app/url"
)

// Specs: http://web.archive.org/web/20060811235523/http://www.mnot.net/drafts/draft-nottingham-atom-format-02.html
type atom03Feed struct {
	ID      string        `xml:"id"`
	Title   atom03Text    `xml:"title"`
	Author  atomPerson    `xml:"author"`
	Links   atomLinks     `xml:"link"`
	Entries []atom03Entry `xml:"entry"`
}

func (a *atom03Feed) Transform() *model.Feed {
	feed := new(model.Feed)
	feed.FeedURL = a.Links.firstLinkWithRelation("self")
	feed.SiteURL = a.Links.originalLink()
	feed.Title = a.Title.String()

	if feed.Title == "" {
		feed.Title = feed.SiteURL
	}

	for _, entry := range a.Entries {
		item := entry.Transform()
		entryURL, err := url.AbsoluteURL(feed.SiteURL, item.URL)
		if err == nil {
			item.URL = entryURL
		}

		if item.Author == "" {
			item.Author = a.Author.String()
		}

		if item.Title == "" {
			item.Title = item.URL
		}

		feed.Entries = append(feed.Entries, item)
	}

	return feed
}

type atom03Entry struct {
	ID       string     `xml:"id"`
	Title    atom03Text `xml:"title"`
	Modified string     `xml:"modified"`
	Issued   string     `xml:"issued"`
	Created  string     `xml:"created"`
	Links    atomLinks  `xml:"link"`
	Summary  atom03Text `xml:"summary"`
	Content  atom03Text `xml:"content"`
	Author   atomPerson `xml:"author"`
}

func (a *atom03Entry) Transform() *model.Entry {
	entry := new(model.Entry)
	entry.URL = a.Links.originalLink()
	entry.Date = a.entryDate()
	entry.Author = a.Author.String()
	entry.Hash = a.entryHash()
	entry.Content = a.entryContent()
	entry.Title = a.entryTitle()
	return entry
}

func (a *atom03Entry) entryTitle() string {
	return sanitizer.StripTags(a.Title.String())
}

func (a *atom03Entry) entryContent() string {
	content := a.Content.String()
	if content != "" {
		return content
	}

	summary := a.Summary.String()
	if summary != "" {
		return summary
	}

	return ""
}

func (a *atom03Entry) entryDate() time.Time {
	dateText := ""
	for _, value := range []string{a.Issued, a.Modified, a.Created} {
		if value != "" {
			dateText = value
			break
		}
	}

	if dateText != "" {
		result, err := date.Parse(dateText)
		if err != nil {
			logger.Error("atom: %v", err)
			return time.Now()
		}

		return result
	}

	return time.Now()
}

func (a *atom03Entry) entryHash() string {
	for _, value := range []string{a.ID, a.Links.originalLink()} {
		if value != "" {
			return crypto.Hash(value)
		}
	}

	return ""
}

type atom03Text struct {
	Type string `xml:"type,attr"`
	Mode string `xml:"mode,attr"`
	Data string `xml:",chardata"`
	XML  string `xml:",innerxml"`
}

func (a *atom03Text) String() string {
	content := ""

	switch {
	case a.Mode == "xml":
		content = a.XML
	case a.Mode == "escaped":
		content = a.Data
	case a.Mode == "base64":
		b, err := base64.StdEncoding.DecodeString(a.Data)
		if err == nil {
			content = string(b)
		}
	default:
		content = a.Data
	}

	if a.Type != "text/html" {
		content = html.EscapeString(content)
	}

	return strings.TrimSpace(content)
}
