package channelserver

import (
	"fmt"

	"github.com/jmoiron/sqlx"
)

const allianceInfoSelectSQL = `
SELECT
ga.id,
ga.name,
created_at,
ga.recruiting,
parent_id,
CASE
	WHEN sub1_id IS NULL THEN 0
	ELSE sub1_id
END,
CASE
	WHEN sub2_id IS NULL THEN 0
	ELSE sub2_id
END
FROM guild_alliances ga
`

// GetAllianceByID loads alliance data including parent and sub guilds.
func (r *GuildRepository) GetAllianceByID(allianceID uint32) (*GuildAlliance, error) {
	rows, err := r.db.Queryx(fmt.Sprintf(`%s WHERE ga.id = $1`, allianceInfoSelectSQL), allianceID)
	if err != nil {
		return nil, err
	}
	defer func() { _ = rows.Close() }()
	if !rows.Next() {
		return nil, nil
	}
	return r.scanAllianceWithGuilds(rows)
}

// ListAlliances returns all alliances with their guild data populated.
func (r *GuildRepository) ListAlliances() ([]*GuildAlliance, error) {
	rows, err := r.db.Queryx(allianceInfoSelectSQL)
	if err != nil {
		return nil, err
	}
	defer func() { _ = rows.Close() }()
	var alliances []*GuildAlliance
	for rows.Next() {
		alliance, err := r.scanAllianceWithGuilds(rows)
		if err != nil {
			continue
		}
		alliances = append(alliances, alliance)
	}
	return alliances, nil
}

// CreateAlliance creates a new guild alliance with the given parent guild.
func (r *GuildRepository) CreateAlliance(name string, parentGuildID uint32) error {
	_, err := r.db.Exec("INSERT INTO guild_alliances (name, parent_id) VALUES ($1, $2)", name, parentGuildID)
	return err
}

// DeleteAlliance removes an alliance by ID.
func (r *GuildRepository) DeleteAlliance(allianceID uint32) error {
	_, err := r.db.Exec("DELETE FROM guild_alliances WHERE id=$1", allianceID)
	return err
}

// RemoveGuildFromAlliance removes a guild from its alliance, shifting sub2 into sub1's slot if needed.
func (r *GuildRepository) RemoveGuildFromAlliance(allianceID, guildID, subGuild1ID, subGuild2ID uint32) error {
	if guildID == subGuild1ID && subGuild2ID > 0 {
		_, err := r.db.Exec(`UPDATE guild_alliances SET sub1_id = sub2_id, sub2_id = NULL WHERE id = $1`, allianceID)
		return err
	} else if guildID == subGuild1ID {
		_, err := r.db.Exec(`UPDATE guild_alliances SET sub1_id = NULL WHERE id = $1`, allianceID)
		return err
	}
	_, err := r.db.Exec(`UPDATE guild_alliances SET sub2_id = NULL WHERE id = $1`, allianceID)
	return err
}

// SetAllianceRecruiting updates whether an alliance is accepting applications.
func (r *GuildRepository) SetAllianceRecruiting(allianceID uint32, recruiting bool) error {
	_, err := r.db.Exec("UPDATE guild_alliances SET recruiting=$1 WHERE id=$2", recruiting, allianceID)
	return err
}

// scanAllianceWithGuilds scans an alliance row and populates its guild data.
func (r *GuildRepository) scanAllianceWithGuilds(rows *sqlx.Rows) (*GuildAlliance, error) {
	alliance := &GuildAlliance{}
	if err := rows.StructScan(alliance); err != nil {
		return nil, err
	}

	parentGuild, err := r.GetByID(alliance.ParentGuildID)
	if err != nil {
		return nil, err
	}
	if parentGuild == nil {
		return nil, fmt.Errorf("alliance %d references non-existent parent guild %d", alliance.ID, alliance.ParentGuildID)
	}
	alliance.ParentGuild = *parentGuild
	alliance.TotalMembers += parentGuild.MemberCount

	if alliance.SubGuild1ID > 0 {
		subGuild1, err := r.GetByID(alliance.SubGuild1ID)
		if err != nil {
			return nil, err
		}
		if subGuild1 == nil {
			return nil, fmt.Errorf("alliance %d references non-existent sub guild 1 %d", alliance.ID, alliance.SubGuild1ID)
		}
		alliance.SubGuild1 = *subGuild1
		alliance.TotalMembers += subGuild1.MemberCount
	}

	if alliance.SubGuild2ID > 0 {
		subGuild2, err := r.GetByID(alliance.SubGuild2ID)
		if err != nil {
			return nil, err
		}
		if subGuild2 == nil {
			return nil, fmt.Errorf("alliance %d references non-existent sub guild 2 %d", alliance.ID, alliance.SubGuild2ID)
		}
		alliance.SubGuild2 = *subGuild2
		alliance.TotalMembers += subGuild2.MemberCount
	}

	return alliance, nil
}
