import { z } from "zod";

const issueTypeSchema = z.object({ name: z.string() });
const statusSchema = z.object({ name: z.string() });

const userSchema = z
  .object({
    accountId: z.string(),
    displayName: z.string().default("Unknown"),
    emailAddress: z.string().optional(),
  })
  .nullable()
  .optional();

const commentSchema = z.object({
  id: z.string(),
  author: z
    .object({
      accountId: z.string(),
      displayName: z.string().default("Unknown"),
      emailAddress: z.string().optional(),
    })
    .optional(),
  body: z.unknown().nullable().default(null),
  created: z.string(),
});

const attachmentSchema = z.object({
  id: z.string(),
  filename: z.string(),
  mimeType: z.string(),
  size: z.number(),
  content: z.string(),
  thumbnail: z.string().nullable().optional(),
  created: z.string(),
});

const subtaskSchema = z.object({
  id: z.string(),
  key: z.string(),
  fields: z.object({
    summary: z.string(),
    issuetype: issueTypeSchema,
    status: statusSchema,
  }),
});

const parentInlineSchema = z
  .object({
    id: z.string(),
    key: z.string(),
    fields: z.object({
      summary: z.string(),
      issuetype: issueTypeSchema,
      status: statusSchema,
    }),
  })
  .optional()
  .nullable();

const baseIssueFieldsSchema = z.object({
  summary: z.string(),
  description: z.unknown().nullable(),
  issuetype: issueTypeSchema,
  status: statusSchema,
  created: z.string().optional(),
  updated: z.string().optional(),
  assignee: userSchema,
  parent: parentInlineSchema,
  subtasks: z.array(subtaskSchema).optional().default([]),
  attachment: z.array(attachmentSchema).optional().default([]),
  comment: z
    .object({
      comments: z.array(commentSchema).optional().default([]),
    })
    .optional(),
  components: z
    .array(z.object({ id: z.string(), name: z.string().optional() }))
    .optional()
    .default([]),
});

// ── Webhook ──────────────────────────────────────────

export const jiraWebhookSchema = z.object({
  webhookEvent: z.string(),
  issue: z.object({
    id: z.string(),
    key: z.string(),
    fields: z.object({
      issuetype: issueTypeSchema,
      status: statusSchema,
      summary: z.string(),
      description: z.unknown().nullable(),
    }),
  }),
});

export type JiraWebhookPayload = z.infer<typeof jiraWebhookSchema>;

// ── Single issue ─────────────────────────────────────

export const jiraIssueResponseSchema = z.object({
  id: z.string(),
  key: z.string(),
  fields: baseIssueFieldsSchema.extend({
    customfield_10035: z.unknown().nullable().optional(),
  }),
});

export type JiraIssueResponse = z.infer<typeof jiraIssueResponseSchema>;

// ── Search ───────────────────────────────────────────

export const jiraSearchResponseSchema = z.object({
  total: z.number().optional().default(0),
  issues: z.array(
    z.object({
      id: z.string(),
      key: z.string(),
      fields: baseIssueFieldsSchema,
    }),
  ),
});

export type JiraSearchResponse = z.infer<typeof jiraSearchResponseSchema>;

// ── Search (paginated, analytics-only lightweight shape) ─────────────

const lightweightStatusSchema = z.object({
  name: z.string(),
  statusCategory: z
    .object({
      key: z.string().optional(),
      name: z.string().optional(),
    })
    .optional(),
});

const lightweightAssigneeSchema = z
  .object({
    accountId: z.string(),
    displayName: z.string().optional(),
    emailAddress: z.string().optional(),
  })
  .nullable()
  .optional();

/**
 * Response shape for `POST /rest/api/3/search/jql` when only analytics-relevant
 * fields are requested. Callers control which fields come back via the `fields`
 * request body; this schema tolerates missing keys.
 */
export const jiraSearchPageResponseSchema = z.object({
  nextPageToken: z.string().optional(),
  isLast: z.boolean().optional(),
  issues: z
    .array(
      z.object({
        id: z.string(),
        key: z.string(),
        fields: z.object({
          summary: z.string().optional(),
          created: z.string().optional(),
          resolutiondate: z.string().nullable().optional(),
          status: lightweightStatusSchema.optional(),
          assignee: lightweightAssigneeSchema,
          project: z
            .object({
              key: z.string(),
              name: z.string().optional(),
            })
            .optional(),
        }),
      }),
    )
    .optional()
    .default([]),
});

export type JiraSearchPageResponse = z.infer<typeof jiraSearchPageResponseSchema>;

// ── Approximate count ────────────────────────────────────────────────

/** `POST /rest/api/3/search/approximate-count` — Jira's replacement for the deprecated `total`. */
export const jiraApproximateCountResponseSchema = z.object({
  count: z.number(),
});

export type JiraApproximateCountResponse = z.infer<typeof jiraApproximateCountResponseSchema>;

// ── Project search (Jira "spaces" = projects) ────────

export const jiraProjectSearchResponseSchema = z.object({
  startAt: z.number().optional().default(0),
  maxResults: z.number().optional().default(50),
  total: z.number().optional().default(0),
  isLast: z.boolean().optional(),
  values: z.array(
    z.object({
      id: z.string(),
      key: z.string(),
      name: z.string(),
      projectTypeKey: z.string().optional(),
    }),
  ),
});

export type JiraProjectSearchResponse = z.infer<typeof jiraProjectSearchResponseSchema>;

// ── Children (parent-based query) ────────────────────

export const jiraChildrenResponseSchema = z.object({
  issues: z.array(
    z.object({
      id: z.string(),
      key: z.string(),
      fields: z.object({
        summary: z.string(),
        issuetype: issueTypeSchema,
        status: statusSchema,
        parent: z.object({ key: z.string() }).optional(),
      }),
    }),
  ),
});
