package mattermost

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log/slog"
	"net/http"
	"net/url"
	"strings"
)

var (
	ErrMissingConfig = errors.New("mattermost adapter missing required configuration")
	ErrInvalidInput  = errors.New("invalid mattermost adapter input")
)

type Config struct {
	BaseURL   string
	Token     string
	ChannelID string
}

type Client struct {
	cfg    Config
	http   *http.Client
	logger *slog.Logger
}

type MessageInput struct {
	ChannelID string
	Text      string
}

type TaskNotificationInput struct {
	TaskID  string
	Title   string
	Status  string
	Message string
}

func NewClient(cfg Config, logger *slog.Logger) *Client {
	return NewClientWithHTTPClient(cfg, http.DefaultClient, logger)
}

func NewClientWithHTTPClient(cfg Config, httpClient *http.Client, logger *slog.Logger) *Client {
	if httpClient == nil {
		httpClient = http.DefaultClient
	}
	return &Client{cfg: cfg, http: httpClient, logger: logger}
}

func (c *Client) SendMessage(ctx context.Context, input MessageInput) error {
	baseURL, token, err := c.config()
	if err != nil {
		return err
	}

	channelID := strings.TrimSpace(input.ChannelID)
	text := strings.TrimSpace(input.Text)
	if channelID == "" || text == "" {
		return ErrInvalidInput
	}

	payload, err := json.Marshal(postRequest{
		ChannelID: channelID,
		Message:   text,
	})
	if err != nil {
		return err
	}

	endpoint := baseURL + "/api/v4/posts"
	req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(payload))
	if err != nil {
		return err
	}
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Accept", "application/json")

	if c.logger != nil {
		c.logger.Info("mattermost message post requested", "channel_id", channelID)
	}

	resp, err := c.http.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	raw, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
	if err != nil {
		return err
	}
	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		return fmt.Errorf("mattermost API request failed (POST /api/v4/posts): status %d: %s", resp.StatusCode, redactedBody(raw, token))
	}

	return nil
}

func (c *Client) SendTaskNotification(ctx context.Context, input TaskNotificationInput) error {
	channelID := strings.TrimSpace(c.cfg.ChannelID)
	if channelID == "" {
		return fmt.Errorf("%w: channel ID", ErrMissingConfig)
	}
	return c.SendMessage(ctx, MessageInput{
		ChannelID: channelID,
		Text:      formatTaskNotification(input),
	})
}

type postRequest struct {
	ChannelID string `json:"channel_id"`
	Message   string `json:"message"`
}

func (c *Client) config() (string, string, error) {
	baseURL := strings.TrimRight(strings.TrimSpace(c.cfg.BaseURL), "/")
	token := strings.TrimSpace(c.cfg.Token)
	if baseURL == "" || token == "" {
		return "", "", fmt.Errorf("%w: base URL and token", ErrMissingConfig)
	}
	parsed, err := url.Parse(baseURL)
	if err != nil {
		return "", "", err
	}
	if parsed.Scheme == "" || parsed.Host == "" {
		return "", "", fmt.Errorf("mattermost base URL must include scheme and host")
	}
	return baseURL, token, nil
}

func formatTaskNotification(input TaskNotificationInput) string {
	title := strings.TrimSpace(input.Title)
	if title == "" {
		title = strings.TrimSpace(input.TaskID)
	}

	var lines []string
	if title == "" {
		lines = append(lines, "NomadCode task completed")
	} else {
		lines = append(lines, "NomadCode task completed: "+title)
	}
	if taskID := strings.TrimSpace(input.TaskID); taskID != "" {
		lines = append(lines, "Task ID: "+taskID)
	}
	if status := strings.TrimSpace(input.Status); status != "" {
		lines = append(lines, "Status: "+status)
	}
	if message := strings.TrimSpace(input.Message); message != "" {
		lines = append(lines, "Message: "+message)
	}
	return strings.Join(lines, "\n")
}

func redactedBody(raw []byte, token string) string {
	message := strings.TrimSpace(string(raw))
	if token != "" {
		message = strings.ReplaceAll(message, token, "<redacted>")
	}
	return message
}
