from datetime import date, datetime
import re

from app.application.company_access import resolve_company_for_user
from app.application.exceptions import ValidationError
from app.domain.entities.settings import CompanyProfile, GeneralSetting
from app.domain.entities.user_registration import UserRegistration
from app.domain.enums import (
    CompanyBusinessType,
    CompanyCountry,
    CompanyIndustryType,
    CompanyTimeZone,
    DateDisplayFormat,
    FiscalYearStart,
    LandingPage,
    SystemCurrency,
    SystemLanguage,
    TimeDisplayFormat,
)
from app.domain.repositories.company_repository import CompanyRepository
from app.domain.repositories.settings_repository import SettingsRepository

CURRENCY_LABELS = {
    SystemCurrency.PKR: "PKR - Pakistani Rupee (Rs.)",
    SystemCurrency.USD: "USD - US Dollar ($)",
    SystemCurrency.EUR: "EUR - Euro (€)",
    SystemCurrency.GBP: "GBP - British Pound (£)",
    SystemCurrency.AED: "AED - UAE Dirham (د.إ)",
    SystemCurrency.SAR: "SAR - Saudi Riyal (﷼)",
    SystemCurrency.INR: "INR - Indian Rupee (₹)",
}

TIME_FORMAT_LABELS = {
    TimeDisplayFormat.HOUR_12: "12 Hour (hh:mm AM/PM)",
    TimeDisplayFormat.HOUR_24: "24 Hour (HH:mm)",
}

LANDING_PAGE_LABELS = {
    LandingPage.DASHBOARD: "Dashboard",
    LandingPage.ITEMS: "Items",
    LandingPage.INVENTORY: "Inventory",
    LandingPage.PURCHASES: "Purchases",
    LandingPage.SALES: "Sales",
    LandingPage.REPORTS: "Reports",
    LandingPage.SETTINGS: "Settings",
}

LANGUAGE_LABELS = {
    SystemLanguage.ENGLISH: "English",
    SystemLanguage.URDU: "Urdu",
    SystemLanguage.ARABIC: "Arabic",
}

ITEMS_PER_PAGE_OPTIONS = [10, 25, 50, 100]
DETAIL_DESCRIPTION_MAX = 20000

BUSINESS_TYPE_LABELS = {
    CompanyBusinessType.SOLE_PROPRIETORSHIP: "Sole Proprietorship",
    CompanyBusinessType.PARTNERSHIP: "Partnership",
    CompanyBusinessType.PRIVATE_LIMITED: "Private Limited",
    CompanyBusinessType.PUBLIC_LIMITED: "Public Limited",
    CompanyBusinessType.LLC: "LLC",
    CompanyBusinessType.NGO: "NGO / Non-Profit",
    CompanyBusinessType.GOVERNMENT: "Government",
    CompanyBusinessType.OTHER: "Other",
}

INDUSTRY_TYPE_LABELS = {
    CompanyIndustryType.RETAIL_DISTRIBUTION: "Retail & Distribution",
    CompanyIndustryType.MANUFACTURING: "Manufacturing",
    CompanyIndustryType.WHOLESALE: "Wholesale",
    CompanyIndustryType.SERVICES: "Services",
    CompanyIndustryType.IT_SOFTWARE: "IT & Software",
    CompanyIndustryType.HEALTHCARE: "Healthcare",
    CompanyIndustryType.CONSTRUCTION: "Construction",
    CompanyIndustryType.AGRICULTURE: "Agriculture",
    CompanyIndustryType.EDUCATION: "Education",
    CompanyIndustryType.OTHER: "Other",
}

FISCAL_YEAR_LABELS = {
    FiscalYearStart.JANUARY: "January",
    FiscalYearStart.FEBRUARY: "February",
    FiscalYearStart.MARCH: "March",
    FiscalYearStart.APRIL: "April",
    FiscalYearStart.MAY: "May",
    FiscalYearStart.JUNE: "June",
    FiscalYearStart.JULY: "July",
    FiscalYearStart.AUGUST: "August",
    FiscalYearStart.SEPTEMBER: "September",
    FiscalYearStart.OCTOBER: "October",
    FiscalYearStart.NOVEMBER: "November",
    FiscalYearStart.DECEMBER: "December",
}

TIME_ZONE_LABELS = {
    CompanyTimeZone.ASIA_KARACHI: "(GMT+05:00) Pakistan Standard Time",
    CompanyTimeZone.ASIA_DUBAI: "(GMT+04:00) Gulf Standard Time",
    CompanyTimeZone.ASIA_RIYADH: "(GMT+03:00) Arabia Standard Time",
    CompanyTimeZone.ASIA_KOLKATA: "(GMT+05:30) India Standard Time",
    CompanyTimeZone.EUROPE_LONDON: "(GMT+00:00) Greenwich Mean Time",
    CompanyTimeZone.AMERICA_NEW_YORK: "(GMT-05:00) Eastern Time",
    CompanyTimeZone.UTC: "(GMT+00:00) UTC",
}

COUNTRY_LABELS = {
    CompanyCountry.PAKISTAN: "Pakistan",
    CompanyCountry.UNITED_ARAB_EMIRATES: "United Arab Emirates",
    CompanyCountry.SAUDI_ARABIA: "Saudi Arabia",
    CompanyCountry.INDIA: "India",
    CompanyCountry.UNITED_STATES: "United States",
    CompanyCountry.UNITED_KINGDOM: "United Kingdom",
    CompanyCountry.CHINA: "China",
    CompanyCountry.OTHER: "Other",
}

PAKISTAN_PROVINCES = [
    "Punjab",
    "Sindh",
    "Khyber Pakhtunkhwa",
    "Balochistan",
    "Islamabad Capital Territory",
    "Gilgit-Baltistan",
    "Azad Jammu & Kashmir",
]


def sanitize_html(value: str | None) -> str | None:
    if not isinstance(value, str):
        return None
    cleaned = re.sub(r"<script[\s\S]*?>[\s\S]*?</script>", "", value, flags=re.I)
    cleaned = re.sub(r"<iframe[\s\S]*?>[\s\S]*?</iframe>", "", cleaned, flags=re.I)
    cleaned = re.sub(r"on\w+\s*=\s*(\"[^\"]*\"|'[^']*'|[^\s>]+)", "", cleaned, flags=re.I)
    cleaned = re.sub(r"javascript:", "", cleaned, flags=re.I)
    cleaned = cleaned.strip()
    if cleaned in {"", "<br>", "<br/>", "<div><br></div>"}:
        return None
    if len(cleaned) > DETAIL_DESCRIPTION_MAX:
        raise ValidationError("Detail description is too long")
    return cleaned


class SettingsService:
    def __init__(
        self,
        repository: SettingsRepository,
        company_repository: CompanyRepository,
    ) -> None:
        self._repository = repository
        self._companies = company_repository

    async def _ensure_company(self, user: UserRegistration, company_id: str) -> None:
        await resolve_company_for_user(self._companies, user, company_id)

    def default_general(self, company_id: str) -> GeneralSetting:
        now = datetime.utcnow()
        return GeneralSetting(
            company_id=company_id,
            site_name="Inventory System",
            site_tagline="Manage your inventory efficiently",
            created_at=now,
            updated_at=now,
        )

    def list_options(self) -> dict:
        return {
            "date_formats": [
                {"key": item.value, "label": item.value} for item in DateDisplayFormat
            ],
            "time_formats": [
                {"key": item.value, "label": TIME_FORMAT_LABELS[item]}
                for item in TimeDisplayFormat
            ],
            "currencies": [
                {"key": item.value, "label": CURRENCY_LABELS[item]}
                for item in SystemCurrency
            ],
            "items_per_page": ITEMS_PER_PAGE_OPTIONS,
            "landing_pages": [
                {"key": item.value, "label": LANDING_PAGE_LABELS[item]}
                for item in LandingPage
            ],
            "languages": [
                {"key": item.value, "label": LANGUAGE_LABELS[item]}
                for item in SystemLanguage
            ],
        }

    async def get_public_branding(self) -> GeneralSetting:
        existing = await self._repository.get_latest_general()
        if existing:
            return existing
        return self.default_general("public")

    async def get_general(self, user: UserRegistration, company_id: str) -> GeneralSetting:
        await self._ensure_company(user, company_id)
        existing = await self._repository.get_general(company_id)
        if existing:
            return existing
        return await self._repository.upsert_general(self.default_general(company_id))

    async def update_general(
        self, user: UserRegistration, company_id: str, data: dict
    ) -> GeneralSetting:
        setting = await self.get_general(user, company_id)

        if data.get("site_name") is not None:
            name = data["site_name"].strip()
            if not name:
                raise ValidationError("Site name is required")
            setting.site_name = name
        if "site_tagline" in data:
            tagline = data.get("site_tagline")
            setting.site_tagline = tagline.strip() if isinstance(tagline, str) and tagline.strip() else None
        if "detail_description" in data:
            setting.detail_description = sanitize_html(data.get("detail_description"))
        if "logo_path" in data:
            setting.logo_path = data.get("logo_path")
        if data.get("date_format") is not None:
            setting.date_format = DateDisplayFormat(data["date_format"])
        if data.get("time_format") is not None:
            setting.time_format = TimeDisplayFormat(data["time_format"])
        if data.get("currency") is not None:
            setting.currency = SystemCurrency(data["currency"])
        if data.get("items_per_page") is not None:
            per_page = int(data["items_per_page"])
            if per_page < 5 or per_page > 100:
                raise ValidationError("Items per page must be between 5 and 100")
            setting.items_per_page = per_page
        if data.get("default_landing_page") is not None:
            setting.default_landing_page = LandingPage(data["default_landing_page"])
        if data.get("system_language") is not None:
            setting.system_language = SystemLanguage(data["system_language"])
        if "enable_multi_warehouse" in data:
            setting.enable_multi_warehouse = bool(data["enable_multi_warehouse"])
        if "enable_barcode_scanning" in data:
            setting.enable_barcode_scanning = bool(data["enable_barcode_scanning"])
        if "enable_stock_alert" in data:
            setting.enable_stock_alert = bool(data["enable_stock_alert"])
        if "enable_batch_expiry" in data:
            setting.enable_batch_expiry = bool(data["enable_batch_expiry"])
        if "allow_negative_stock" in data:
            setting.allow_negative_stock = bool(data["allow_negative_stock"])
        if "show_product_images" in data:
            setting.show_product_images = bool(data["show_product_images"])

        setting.updated_at = datetime.utcnow()
        return await self._repository.upsert_general(setting)

    async def delete_logo(self, user: UserRegistration, company_id: str) -> GeneralSetting:
        return await self.update_general(user, company_id, {"logo_path": None})

    def list_company_profile_options(self) -> dict:
        return {
            "business_types": [
                {"key": item.value, "label": BUSINESS_TYPE_LABELS[item]}
                for item in CompanyBusinessType
            ],
            "industry_types": [
                {"key": item.value, "label": INDUSTRY_TYPE_LABELS[item]}
                for item in CompanyIndustryType
            ],
            "currencies": [
                {"key": item.value, "label": CURRENCY_LABELS[item]}
                for item in SystemCurrency
            ],
            "fiscal_year_starts": [
                {"key": item.value, "label": FISCAL_YEAR_LABELS[item]}
                for item in FiscalYearStart
            ],
            "time_zones": [
                {"key": item.value, "label": TIME_ZONE_LABELS[item]}
                for item in CompanyTimeZone
            ],
            "languages": [
                {"key": item.value, "label": LANGUAGE_LABELS[item]}
                for item in SystemLanguage
            ],
            "countries": [
                {"key": item.value, "label": COUNTRY_LABELS[item]}
                for item in CompanyCountry
            ],
            "states": PAKISTAN_PROVINCES,
        }

    def _blank_to_none(self, value: str | None) -> str | None:
        if value is None:
            return None
        cleaned = value.strip()
        return cleaned or None

    def _require_text(self, value: str | None, field: str) -> str:
        cleaned = (value or "").strip()
        if not cleaned:
            raise ValidationError(f"{field} is required")
        return cleaned

    async def default_company_profile(self, company_id: str) -> CompanyProfile:
        company = await self._companies.get_by_id(company_id)
        general = await self._repository.get_general(company_id)
        now = datetime.utcnow()
        logo = None
        if company and company.logo and company.logo not in {"", "n/a"}:
            logo = company.logo
        return CompanyProfile(
            company_id=company_id,
            company_name=(company.name if company else "Inventory System (Pvt.) Ltd."),
            company_tagline=(
                general.site_tagline if general else "Manage your inventory efficiently"
            ),
            currency=general.currency if general else SystemCurrency.PKR,
            default_language=(
                general.system_language if general else SystemLanguage.ENGLISH
            ),
            logo_path=logo,
            address=(company.address if company else ""),
            country=CompanyCountry.PAKISTAN,
            fiscal_year_start=FiscalYearStart.JULY,
            time_zone=CompanyTimeZone.ASIA_KARACHI,
            created_at=now,
            updated_at=now,
        )

    async def get_company_profile(
        self, user: UserRegistration, company_id: str
    ) -> CompanyProfile:
        await self._ensure_company(user, company_id)
        existing = await self._repository.get_company_profile(company_id)
        if existing:
            return existing
        return await self._repository.upsert_company_profile(
            await self.default_company_profile(company_id)
        )

    async def update_company_profile(
        self, user: UserRegistration, company_id: str, data: dict
    ) -> CompanyProfile:
        profile = await self.get_company_profile(user, company_id)

        if data.get("company_name") is not None:
            profile.company_name = self._require_text(data["company_name"], "Company name")
        if "date_of_establishment" in data:
            value = data.get("date_of_establishment")
            if isinstance(value, str) and value.strip():
                profile.date_of_establishment = date.fromisoformat(value.strip()[:10])
            elif isinstance(value, date):
                profile.date_of_establishment = value
            else:
                profile.date_of_establishment = None
        if "company_tagline" in data:
            profile.company_tagline = self._blank_to_none(data.get("company_tagline"))
        if "business_type" in data:
            value = data.get("business_type")
            profile.business_type = CompanyBusinessType(value) if value else None
        if "industry_type" in data:
            value = data.get("industry_type")
            profile.industry_type = CompanyIndustryType(value) if value else None
        if data.get("currency") is not None:
            profile.currency = SystemCurrency(data["currency"])
        if "registration_number" in data:
            profile.registration_number = self._blank_to_none(data.get("registration_number"))
        if data.get("fiscal_year_start") is not None:
            profile.fiscal_year_start = FiscalYearStart(data["fiscal_year_start"])
        if "tax_number" in data:
            profile.tax_number = self._blank_to_none(data.get("tax_number"))
        if data.get("time_zone") is not None:
            profile.time_zone = CompanyTimeZone(data["time_zone"])
        if "website" in data:
            profile.website = self._blank_to_none(data.get("website"))
        if data.get("default_language") is not None:
            profile.default_language = SystemLanguage(data["default_language"])
        if "logo_path" in data:
            profile.logo_path = data.get("logo_path")
        if data.get("address") is not None:
            profile.address = self._require_text(data["address"], "Address")
        if data.get("country") is not None:
            profile.country = CompanyCountry(data["country"])
        if data.get("state_province") is not None:
            profile.state_province = self._require_text(
                data["state_province"], "State / Province"
            )
        if data.get("city") is not None:
            profile.city = self._require_text(data["city"], "City")
        if data.get("postal_code") is not None:
            profile.postal_code = self._require_text(data["postal_code"], "Postal / Zip Code")
        if "phone" in data:
            profile.phone = self._blank_to_none(data.get("phone"))
        if "alternate_phone" in data:
            profile.alternate_phone = self._blank_to_none(data.get("alternate_phone"))
        if "email" in data:
            profile.email = self._blank_to_none(data.get("email"))
        if "alternate_email" in data:
            profile.alternate_email = self._blank_to_none(data.get("alternate_email"))
        if "fax" in data:
            profile.fax = self._blank_to_none(data.get("fax"))
        if "number_of_employees" in data:
            profile.number_of_employees = self._blank_to_none(data.get("number_of_employees"))
        if "description" in data:
            profile.description = self._blank_to_none(data.get("description"))
        if "notes" in data:
            profile.notes = self._blank_to_none(data.get("notes"))
        if "facebook_url" in data:
            profile.facebook_url = self._blank_to_none(data.get("facebook_url"))
        if "twitter_url" in data:
            profile.twitter_url = self._blank_to_none(data.get("twitter_url"))
        if "linkedin_url" in data:
            profile.linkedin_url = self._blank_to_none(data.get("linkedin_url"))
        if "instagram_url" in data:
            profile.instagram_url = self._blank_to_none(data.get("instagram_url"))
        if "youtube_url" in data:
            profile.youtube_url = self._blank_to_none(data.get("youtube_url"))
        if "whatsapp" in data:
            profile.whatsapp = self._blank_to_none(data.get("whatsapp"))

        profile.updated_at = datetime.utcnow()
        saved = await self._repository.upsert_company_profile(profile)
        await self._sync_company_record(company_id, saved)
        return saved

    async def delete_company_profile_logo(
        self, user: UserRegistration, company_id: str
    ) -> CompanyProfile:
        return await self.update_company_profile(user, company_id, {"logo_path": None})

    async def _sync_company_record(self, company_id: str, profile: CompanyProfile) -> None:
        company = await self._companies.get_by_id(company_id)
        if not company or not company.id:
            return
        company.name = profile.company_name
        company.address = profile.address
        if profile.logo_path:
            company.logo = profile.logo_path
        company.updated_at = datetime.utcnow()
        await self._companies.update(company.id, company)

