from datetime import datetime


def normalize_linkedin_url(value):
    value = clean_text(value).split("?", 1)[0].split("#", 1)[0].rstrip("/")
    return value.lower() or None


def parse_apify_date(value):
    parsed = parse_apify_datetime(value)
    if parsed is None:
        return None
    return parsed.astimezone().date()


def parse_apify_datetime(value):
    if not value:
        return None

    normalized = value.replace("Z", "+00:00")
    try:
        parsed = datetime.fromisoformat(normalized)
    except ValueError:
        return None
    return parsed


def clean_text(value):
    if value is None:
        return ""
    return " ".join(str(value).split())


def limit_text(value, limit):
    value = clean_text(value)
    if not value:
        return None
    return value[:limit]


def nullable_text(value):
    value = clean_text(value)
    return value or None


def int_or_none(value):
    if value is None or value == "":
        return None
    try:
        return int(str(value).replace(",", ""))
    except (TypeError, ValueError):
        return None


def labeled_line(label, value):
    value = nullable_text(value)
    if value is None:
        return None
    return "{}: {}".format(label, value)


def format_period(item):
    start = (item.get("startDate") or {}).get("text")
    end = (item.get("endDate") or {}).get("text")
    if start and end:
        return "{} - {}".format(start, end)
    return item.get("period") or start or end or ""


def join_lines(lines):
    text = "\n".join(line for line in lines if line)
    return text or None


def format_experience(items):
    lines = []
    for item in items or []:
        title = item.get("position") or item.get("title")
        company = item.get("companyName")
        period = format_period(item)
        description = clean_text(item.get("description"))

        first_line = " - ".join(part for part in [title, company, period] if part)
        if first_line:
            lines.append(first_line)
        if description:
            lines.append(description)

    return join_lines(lines)


def format_education(items):
    lines = []
    for item in items or []:
        school = item.get("schoolName")
        degree = item.get("degree")
        field = item.get("fieldOfStudy")
        period = format_period(item)

        line = " - ".join(part for part in [school, degree, field, period] if part)
        if line:
            lines.append(line)

    return join_lines(lines)


def format_certifications(items):
    lines = []
    for item in items or []:
        title = item.get("title") or item.get("name")
        issued_by = item.get("issuedBy") or item.get("authority") or item.get("companyName")
        issued_at = item.get("issuedAt")

        line = " - ".join(part for part in [title, issued_by, issued_at] if part)
        if line:
            lines.append(line)

    return join_lines(lines)


def format_skills(profile):
    top_skills = profile.get("topSkills")
    if top_skills:
        return clean_text(top_skills)
    return None


def parse_profile_items(items):
    if not items:
        return None

    profile = items[0]
    first_name = clean_text(profile.get("firstName"))
    last_name = clean_text(profile.get("lastName"))
    full_name = clean_text("{} {}".format(first_name, last_name))
    linkedin_url = nullable_text(profile.get("linkedinUrl"))
    location = nullable_text((profile.get("location") or {}).get("linkedinText"))
    headline = nullable_text(profile.get("headline"))

    connections = int_or_none(profile.get("connectionsCount"))
    followers = int_or_none(profile.get("followerCount"))
    total_connections = None
    if connections is not None and followers is not None:
        total_connections = connections + followers

    experience = format_experience(profile.get("experience") or profile.get("currentPosition") or [])
    education = format_education(profile.get("education") or profile.get("profileTopEducation") or [])
    certifications = format_certifications(profile.get("certifications") or [])
    verified = profile.get("verified")

    return {
        "linked_profile": join_lines([
            labeled_line("FULL NAME", full_name),
            labeled_line("LINKEDIN", linkedin_url),
            labeled_line("LOCATION", location),
            labeled_line("HEADLINE", headline),
        ]),
        "linked_vrfd": "Y" if verified is True else "N",
        "linked_since": parse_apify_date(profile.get("registeredAt")),
        "linked_all_info": join_lines([experience, education, certifications]),
        "linked_con": connections,
        "linked_folower": followers,
        "linked_con_tol": total_connections,
        "prosp_exp": experience,
        "prosp_edu": education,
        "prosp_cert": certifications,
        "prosp_skills": format_skills(profile),
    }


def profile_item_key(item):
    original_query = item.get("originalQuery") or {}
    return (
        normalize_linkedin_url(original_query.get("query"))
        or normalize_linkedin_url(item.get("linkedinUrl"))
    )


def activity_item_key(item):
    return (
        normalize_linkedin_url(item.get("source_profile"))
        or normalize_linkedin_url(item.get("profile_input"))
        or normalize_linkedin_url(((item.get("commenter") or {}).get("linkedin_url")))
    )


def group_items_by_profile(items, key_func):
    grouped = {}
    for item in items or []:
        key = key_func(item)
        if key:
            grouped.setdefault(key, []).append(item)
    return grouped


def parse_activity_items(items):
    activities = []
    latest_datetime = None
    latest_date = None

    for item in items or []:
        is_comment = bool(item.get("comment_text"))
        act_type = "comment" if is_comment else "reaction"

        if is_comment:
            raw_date = ((item.get("created_at") or {}).get("formatted"))
            snippet = item.get("comment_text")
        else:
            post = item.get("post") or {}
            raw_date = ((post.get("created_at") or {}).get("formatted"))
            snippet = post.get("post_text")

        act_datetime = parse_apify_datetime(raw_date)
        activity = {
            "act_type": act_type,
            "act_type_info": limit_text(snippet, 200),
            "linked_act_dt": act_datetime.astimezone().date() if act_datetime else None,
        }

        if activity["act_type_info"] or activity["linked_act_dt"]:
            activities.append(activity)

        if act_datetime and (latest_datetime is None or act_datetime > latest_datetime):
            latest_datetime = act_datetime
            latest_date = activity["linked_act_dt"]

    return activities, latest_date
