"""
NRI Property Guard — Website Functional Test Suite (Selenium / Python)
============================================================================

This is the Selenium counterpart to tests/test_playwright_suite.py. It was
written to run in any normal environment with Chrome/Chromium and a matching
chromedriver (or Selenium 4's built-in Selenium Manager, which auto-resolves
one). It could NOT be executed inside the sandbox used to build this site —
see README.md in this folder for exactly why — so the Playwright suite is
what actually produced the pass/fail results in the QA report. This file is
still real, complete, runnable code: point it at your own environment (local
machine or CI) and it will drive an actual browser end to end.

Usage:
    pip install -r requirements.txt
    python3 -m http.server 8899          # from the site/ root, in another terminal
    python3 test_functionality.py         # or: pytest test_functionality.py -v

Environment variables:
    BASE_URL        Override the site under test (default http://localhost:8899)
    RUN_LIVE_LEAD_SUBMIT
                     Set to "1" to actually let the contact form POST to the
                     live Supabase website_leads table. Left unset (default),
                     the lead-form test only fills and validates the form and
                     does NOT submit it, so running this suite never writes
                     synthetic rows into production data.

Note on role-based dashboard routing (Admin / Property Manager / Property
Executive / Customer, each landing on its own dashboard after login): that
regression coverage lives only in the Playwright suite, via a fully mocked
Supabase client (see tests/test_playwright_suite.py, section 8b). It isn't
duplicated here because safely exercising four different account roles needs
either real staff credentials for each role (which this suite should not
require just to run) or the same kind of full network-level client stub the
Playwright suite already uses — Selenium has no equivalent to Playwright's
page.route() for swapping out a whole same-origin script file, so replicating
that stub here would mean a materially different technique, not a port of the
existing tests. If you want this covered end-to-end against a real Chrome
driver, the straightforward path is real test accounts for each of the five
roles, signing in as each and asserting on the landing URL.
"""

import json
import os
import time
import unittest

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException, NoSuchElementException

BASE_URL = os.environ.get("BASE_URL", "http://localhost:8899")
RUN_LIVE_LEAD_SUBMIT = os.environ.get("RUN_LIVE_LEAD_SUBMIT") == "1"

MARKETING_PAGES = [
    "/index.html", "/services.html", "/how-it-works.html", "/pricing.html",
    "/about.html", "/faq.html", "/contact.html",
    "/privacy-policy.html", "/terms.html",
]


def make_driver(mobile=False):
    options = webdriver.ChromeOptions()
    options.add_argument("--headless=new")
    options.add_argument("--no-sandbox")
    options.add_argument("--disable-gpu")
    if mobile:
        options.add_argument("--window-size=390,844")
    else:
        options.add_argument("--window-size=1280,900")
    # Selenium 4's Selenium Manager will auto-resolve a matching chromedriver.
    # If your environment has one on PATH already, this still works unchanged.
    return webdriver.Chrome(options=options)


class SiteLoadsCleanly(unittest.TestCase):
    """Every page returns 200-equivalent content, has exactly one <h1>, no JS errors."""

    @classmethod
    def setUpClass(cls):
        cls.driver = make_driver()
        cls.wait = WebDriverWait(cls.driver, 10)

    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()

    def _get_console_errors(self):
        try:
            logs = self.driver.get_log("browser")
        except Exception:
            return []  # not all drivers/browsers expose this the same way
        return [l["message"] for l in logs if l["level"] == "SEVERE"
                and "ERR_TUNNEL_CONNECTION_FAILED" not in l["message"]
                and "fonts.googleapis.com" not in l["message"]]

    def test_all_pages_load_with_single_h1(self):
        for path in MARKETING_PAGES + ["/portal/index.html"]:
            with self.subTest(path=path):
                self.driver.get(BASE_URL + path)
                self.assertIn("NRI Property Guard", self.driver.title)
                h1s = self.driver.find_elements(By.TAG_NAME, "h1")
                self.assertEqual(len(h1s), 1, f"{path} should have exactly one <h1>")
                errors = self._get_console_errors()
                self.assertEqual(errors, [], f"Unexpected console errors on {path}: {errors}")


class SeoMetadata(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.driver = make_driver()

    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()

    def test_marketing_pages_have_seo_tags_and_are_indexable(self):
        for path in MARKETING_PAGES:
            with self.subTest(path=path):
                self.driver.get(BASE_URL + path)
                title = self.driver.title
                self.assertGreater(len(title), 10)

                desc = self.driver.find_element(By.CSS_SELECTOR, 'meta[name="description"]')
                self.assertGreater(len(desc.get_attribute("content")), 20)

                canonical = self.driver.find_element(By.CSS_SELECTOR, 'link[rel="canonical"]')
                self.assertTrue(canonical.get_attribute("href").startswith("https://"))

                og_title = self.driver.find_elements(By.CSS_SELECTOR, 'meta[property="og:title"]')
                og_image = self.driver.find_elements(By.CSS_SELECTOR, 'meta[property="og:image"]')
                self.assertTrue(og_title and og_image, "Missing Open Graph tags")

                robots_tags = self.driver.find_elements(By.CSS_SELECTOR, 'meta[name="robots"]')
                if robots_tags:
                    self.assertNotIn("noindex", robots_tags[0].get_attribute("content"))

    def test_portal_login_is_noindex(self):
        self.driver.get(BASE_URL + "/portal/index.html")
        robots = self.driver.find_element(By.CSS_SELECTOR, 'meta[name="robots"]')
        self.assertIn("noindex", robots.get_attribute("content"))

    def test_home_has_organization_structured_data(self):
        self.driver.get(BASE_URL + "/index.html")
        scripts = self.driver.find_elements(By.CSS_SELECTOR, 'script[type="application/ld+json"]')
        found = False
        for s in scripts:
            data = json.loads(s.get_attribute("innerHTML"))
            if data.get("@type") == "Organization":
                found = True
        self.assertTrue(found)

    def test_faq_has_valid_faqpage_structured_data(self):
        self.driver.get(BASE_URL + "/faq.html")
        scripts = self.driver.find_elements(By.CSS_SELECTOR, 'script[type="application/ld+json"]')
        found = False
        for s in scripts:
            data = json.loads(s.get_attribute("innerHTML"))  # raises if not valid JSON
            if data.get("@type") == "FAQPage":
                self.assertGreaterEqual(len(data.get("mainEntity", [])), 5)
                found = True
        self.assertTrue(found)


class WhatsAppWidget(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.driver = make_driver()
        cls.wait = WebDriverWait(cls.driver, 10)

    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()

    def setUp(self):
        self.driver.get(BASE_URL + "/index.html")

    def test_fab_present_and_opens_panel(self):
        fab = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, ".wa-fab")))
        fab.click()
        panel = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, ".wa-panel.open")))
        self.assertTrue(panel.is_displayed())

    def test_whatsapp_link_targets_correct_number(self):
        self.driver.find_element(By.CSS_SELECTOR, ".wa-fab").click()
        link = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, ".wa-action.wa")))
        href = link.get_attribute("href")
        self.assertTrue(href.startswith("https://wa.me/919948039325"))

    def test_call_link_targets_correct_number(self):
        self.driver.find_element(By.CSS_SELECTOR, ".wa-fab").click()
        call_link = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.wa-action[href^="tel:"]')))
        self.assertEqual(call_link.get_attribute("href"), "tel:+919948039325")

    def test_close_button_closes_panel(self):
        self.driver.find_element(By.CSS_SELECTOR, ".wa-fab").click()
        self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, ".wa-panel.open")))
        self.driver.find_element(By.CSS_SELECTOR, ".wa-close").click()
        time.sleep(0.3)
        panels = self.driver.find_elements(By.CSS_SELECTOR, ".wa-panel.open")
        self.assertEqual(len(panels), 0)


class MobileNavigation(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.driver = make_driver(mobile=True)
        cls.wait = WebDriverWait(cls.driver, 10)

    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()

    def test_toggle_visible_and_opens_menu(self):
        self.driver.get(BASE_URL + "/index.html")
        toggle = self.wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, ".nav-toggle")))
        toggle.click()
        time.sleep(0.3)
        nav = self.driver.find_element(By.CSS_SELECTOR, ".nav-links")
        self.assertIn("open", nav.get_attribute("class"))

    def test_owner_login_link_never_falsely_active_on_home(self):
        self.driver.get(BASE_URL + "/index.html")
        active_portal_links = self.driver.find_elements(
            By.CSS_SELECTOR, 'a[href="portal/index.html"].active'
        )
        self.assertEqual(len(active_portal_links), 0,
                          "Owner Login link should never carry .active styling on the home page")


class FaqAccordion(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.driver = make_driver()
        cls.wait = WebDriverWait(cls.driver, 10)

    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()

    def test_second_item_opens_and_closes(self):
        self.driver.get(BASE_URL + "/faq.html")
        items = self.driver.find_elements(By.CSS_SELECTOR, ".accordion-item")
        item = items[1]  # item 0 renders pre-opened by design
        self.assertNotIn("open", item.get_attribute("class"))

        item.find_element(By.CSS_SELECTOR, ".accordion-q").click()
        time.sleep(0.3)
        item = self.driver.find_elements(By.CSS_SELECTOR, ".accordion-item")[1]
        self.assertIn("open", item.get_attribute("class"))

        item.find_element(By.CSS_SELECTOR, ".accordion-q").click()
        time.sleep(0.3)
        item = self.driver.find_elements(By.CSS_SELECTOR, ".accordion-item")[1]
        self.assertNotIn("open", item.get_attribute("class"))


class ContactLeadForm(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.driver = make_driver()
        cls.wait = WebDriverWait(cls.driver, 10)

    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()

    def test_form_fields_present_and_required(self):
        self.driver.get(BASE_URL + "/contact.html")
        name = self.driver.find_element(By.ID, "c-name")
        email = self.driver.find_element(By.ID, "c-email")
        self.assertEqual(name.get_attribute("required"), "true")
        self.assertEqual(email.get_attribute("type"), "email")
        self.assertEqual(email.get_attribute("required"), "true")

    @unittest.skipUnless(RUN_LIVE_LEAD_SUBMIT,
                          "Set RUN_LIVE_LEAD_SUBMIT=1 to allow this test to write a real row "
                          "to the live website_leads table. Skipped by default to protect "
                          "production data — see module docstring.")
    def test_submit_shows_success_message_live(self):
        self.driver.get(BASE_URL + "/contact.html")
        self.driver.find_element(By.ID, "c-name").send_keys("Selenium QA Test")
        self.driver.find_element(By.ID, "c-email").send_keys("selenium-qa@example.com")
        self.driver.find_element(By.ID, "c-msg").send_keys("Automated Selenium test submission — safe to ignore/delete.")
        self.driver.find_element(By.CSS_SELECTOR, 'button[type="submit"]').click()
        alert = self.wait.until(
            EC.presence_of_element_located((By.CSS_SELECTOR, ".form-alert.success"))
        )
        self.assertIn("Thank you", alert.text)


class PortalAuthScreen(unittest.TestCase):
    """
    Read-only checks against the LIVE Supabase project (a getSession() call
    only — no writes, no account created) plus pure client-side UI checks.
    """

    @classmethod
    def setUpClass(cls):
        cls.driver = make_driver()
        cls.wait = WebDriverWait(cls.driver, 10)

    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()

    def test_supabase_client_initializes_and_session_check_completes(self):
        self.driver.get(BASE_URL + "/portal/index.html")
        try:
            self.wait.until(
                lambda d: "display: none" not in
                d.find_element(By.ID, "authArea").get_attribute("style")
            )
        except TimeoutException:
            self.fail("authArea never became visible — Supabase client failed to initialize "
                      "or the session check hung (check for a stale/CDN-blocked supabase-js).")

    def test_register_tab_shows_tier_picker(self):
        self.driver.get(BASE_URL + "/portal/index.html")
        self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '.auth-tab[data-tab="register"]'))).click()
        tier_picker = self.wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, ".tier-picker")))
        self.assertTrue(tier_picker.is_displayed())

    def test_tier_picker_prime_is_selectable(self):
        self.driver.get(BASE_URL + "/portal/index.html")
        self.driver.find_element(By.CSS_SELECTOR, '.auth-tab[data-tab="register"]').click()
        prime = self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '.tier-opt[data-tier="prime"]')))
        prime.click()
        time.sleep(0.2)
        self.assertIn("selected", prime.get_attribute("class"))

    def test_password_field_is_masked(self):
        self.driver.get(BASE_URL + "/portal/index.html")
        self.driver.find_element(By.CSS_SELECTOR, '.auth-tab[data-tab="register"]').click()
        pw = self.driver.find_element(By.ID, "r-pass")
        self.assertEqual(pw.get_attribute("type"), "password")


class RobotsAndSitemap(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls.driver = make_driver()

    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()

    def test_robots_txt_disallows_dashboard(self):
        self.driver.get(BASE_URL + "/robots.txt")
        body = self.driver.find_element(By.TAG_NAME, "body").text
        self.assertIn("Disallow: /portal/dashboard.html", body)
        self.assertIn("Sitemap:", body)

    def test_sitemap_lists_marketing_pages(self):
        self.driver.get(BASE_URL + "/sitemap.xml")
        body = self.driver.page_source
        self.assertIn("<urlset", body)
        for path in MARKETING_PAGES:
            slug = path.lstrip("/")
            if slug == "index.html":
                self.assertIn("nripropertyguard.in/", body)
            else:
                self.assertIn(slug, body)


if __name__ == "__main__":
    unittest.main(verbosity=2)
